Jetpack架構組件學習(4)——APP Startup庫的使用

最近在研究APP的啓動優化,也是發現了Jetpack中的App Startup庫,可以進行SDK的初始化操作,於是便是學習了,特此記錄

原文:Jetpack架構組件學習(4)——App Startup庫的使用 - Stars-One的雜貨小窩

兩種方式初始化SDK

首先,先是講解了關於SDK的初始化方式,像我們一般使用到百度地圖,或者某些開源庫的時候,需要我們進行初始化操作

而他們文檔給出的大多數爲以下方式:

自定義一個Application,在Application對應的生命週期OnCreate()方法中進行初始化操作

這樣一樣,一旦項目接入的SDK過多,Application裏的初始化邏輯也是多了起來,且代碼層面,每次都要去加代碼,也是十分繁瑣.

針對代碼繁瑣的問題,有的開源庫作者找到了一種比較取巧的方法,那就是通過四大組件中的ContentProvider進行初始化操作

具體怎麼做呢?

首先,創建一個ContentProvider類:

package site.starsone.abdemo

import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import com.blankj.utilcode.util.LogUtils

class MyContentProvider: ContentProvider() {
    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        TODO("Not yet implemented")
    }

    override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor? {
        TODO("Not yet implemented")
    }

    override fun onCreate(): Boolean {
       context?.let {
           LogUtils.d("初始化了...")
       }
        return true
    }

    override fun update(
        uri: Uri,
        values: ContentValues?,
        selection: String?,
        selectionArgs: Array<out String>?
    ): Int {
        TODO("Not yet implemented")
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
        TODO("Not yet implemented")
    }

    override fun getType(uri: Uri): String? {
        TODO("Not yet implemented")
    }

}

PS:上面,因爲我們只是爲了初始化,所以只給了onCreate()其他的方法直接留空不寫即可

之後,還需要在AndroidManifest.xml文件中進行聲明

<application ...>

    <provider
        android:name=".MyContentProvider"
        android:authorities="${applicationId}.myProvider"
        android:exported="false" />

</application>

authorities在這裏並沒有固定的要求,填寫什麼值都是可以的,但必須保證這個值在整個手機上是唯一的,

所以通常會使用${applicationId}作爲前綴,以防止和其他應用程序衝突。

運行結果如下所示:

關於運行的流程,如下圖所示

一個應用程序的執行順序是這個樣子的。首先調用ApplicationattachBaseContext()方法,

然後調用ContentProvideronCreate()方法,接下來調用Application的onCreate()方法。

我們使用上述方法治好,在提供庫給別的開發者用的時候,別的開發者就不用再去多寫一步初始化的操作了,簡化了其他開發者使用庫的操作流程。

上面的方法雖然是比較巧妙,但是ContentProvider會增加許多額外的耗時

一個空白的ContentProvider會多出2ms的加載時間,實際項目複雜多一多,加載速度豈不是直接拖慢?

這有什麼解決方法呢?

當然有,官方也是發現了開發者通過ContentProvider來取巧進行初始化的步驟,於是進行了進一步的封裝,於是就是今天所講的App Startup:

它可以將所有用於初始化的ContentProvider合併成一個,從而使App的啓動速度變得更快。

話說是不是官方已經變相承認了這種取巧方式哈哈 😂

App Startup基本使用

1.引入依賴

使用之前,老慣例了,引入依賴

implementation 'androidx.startup:startup-runtime:1.0.0'

這裏需要注意: 如果使用最新的1.1.1版本,要求最低的SDK版本(compileSdk)要求爲31

關於已發佈版本,可以點擊此鏈接進行查看jetpack各庫版本

2.實現Initializer接口

//Java中這個Unit是Void類型
class SdkAInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        LogUtils.d("Sdk A 初始化")
    }

    //默認實現方法返回MutableList,將其改爲List
    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}

3.聲明provider

AndroidManifest.xml文件中聲明:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="site.starsone.abdemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AbDemo">
        ...

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data
                android:name="com.site.starsone.abdemo.SdkAInitializer"
                android:value="androidx.startup" />
        </provider>
    </application>

</manifest>

注意點:

  • 只需要添加對應的meta-data的內容,並更改name屬性即可,其他的代碼不需要變動
  • tools:node需要application標籤上聲明tools的命名空間
  • meta-data裏的android:name要輸入class全類名(因爲App Startup本質上也是通過反射來找到你的這個類)

效果如下圖所示:

進階使用(依賴關係)

我們再添加一個SdkBInitializer的初始化類:

class SdkBInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        LogUtils.d("Sdk B 初始化")
    }

    //默認實現方法返回MutableList,將其改爲List
    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}

代碼實際上與SdkAInitializer一樣,上面基本使用沒有擴展講解,這裏先講解一下

dependencies()方法是用來說明當前的SdkBInitializer是否需要前置的某個依賴初始化完畢才執行

我們添加了SdkBInitializer,和之前一樣,也得聲明

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="site.starsone.abdemo.SdkBInitializer"
        android:value="androidx.startup" />
    <meta-data
        android:name="site.starsone.abdemo.SdkAInitializer"
        android:value="androidx.startup" />
</provider>

這裏就聲明瞭A和B兩個SDK的初始化類,本來我以爲聲明的順序會影響初始化的順序,實際上並不影響

以下截圖就是上述代碼運行的效果:

於是,我們現在要將上面的順序變更爲以下:

SDKA的初始化,需要SDKB初始化完之後才能進行,於是我們取修改SdkAInitializer的代碼

class SdkAInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        LogUtils.d("Sdk A 初始化")
    }

    //默認實現方法返回MutableList,將其改爲List
    override fun dependencies(): List<Class<out Initializer<*>>> {
        //這裏回傳依賴項回去
        return listOf(SdkBInitializer::class.java)
    }
}

運行結果:

PS: 如果項目中的只有兩個依賴的初始化,且這兩個初始化有先後順序,在AndroidManifest.xml文件可以只寫一個

如上面的例子,由於SDKA是要在SDKB初始化後才初始化,所以只用聲明SDKA即可(因爲代碼裏已經返回了對應的List,會被找到,無需聲明)

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="site.starsone.abdemo.SdkAInitializer"
        android:value="androidx.startup" />
</provider>

進階使用(手動初始化)

上面說明的是自動初始化,考慮到還會有手動初始化的需求

1.取消自動初始化

首先,我們得先配置AndroidManifest.xml文件中的屬性,取消初始化

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        tools:node="remove"
        android:name="site.starsone.abdemo.SdkAInitializer"
        android:value="androidx.startup" />
</provider>

tools:node="remove"而不是直接移除這個標籤,是爲了確保清單合併工具能夠移除所有合併文件的這個標籤。

在meta-data標籤中添加tools:node="remove",是禁止單個組件的自動初始化

如果想要禁止所有組件,則是在provider標籤上加上屬性

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="remove">
    <meta-data
        android:name="site.starsone.abdemo.SdkAInitializer"
        android:value="androidx.startup" />
</provider>

PS: 實際上測試發現,如果你不想你的組件被自動初始化,直接刪除對應的meta-data標籤即可

2.代碼中進行初始化

至於初始化,則是在代碼中進行即可,具體的時機由開發者來定,比如說在點擊某個按鈕之後

只需要傳對應初始化的類即可,代碼如下:

AppInitializer.getInstance(this).initializeComponent(SdkBInitializer::class.java)

上述代碼是在Activity中進行的,所以使用的this

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章