亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android使用ContentProvider初始化SDK庫方案小結(jié)

 更新時(shí)間:2021年04月26日 08:59:38   作者:灰色飄零  
這篇文章主要介紹了Android使用ContentProvider初始化SDK庫方案總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

做Android SDK開發(fā)的時(shí)候,一般我們會將初始化的方法封裝為,然后讓調(diào)用SDK的開發(fā)者在Application的onCreate方法中進(jìn)行初始化。但是目前一些主流的SDK框架,并沒有提供相關(guān)的方法進(jìn)行初始化,但是我們在使用的時(shí)候也能正常使用,通過挖掘其源碼,可以看出來他們一般使用的ContentProvider來進(jìn)行SDK的初始化的,目前使用ContentProvider的知名SDK有:ButterKnife、Leakcanary、BlockCanary...等等。

這里補(bǔ)充一個(gè)概念,SDK初始化的本質(zhì)是什么?

SDK初始化的本質(zhì)是將App的上下文(Context)注入到SDK中,使其能通過這個(gè)上下文訪問到App的資源與服務(wù)。也包括在初始化時(shí)調(diào)用SDK方法進(jìn)行相關(guān)選項(xiàng)的自定義配置。

一、ContentProvider初始化SDK庫的實(shí)現(xiàn)

要實(shí)現(xiàn)在ContentProvider初始化SDK庫,首先要在庫中創(chuàng)建一個(gè) ContentProvider,然后在 ContentProvider 的 onCreate() 方法中借助 getContext() 返回的 Context 來完成你的庫初始化,當(dāng)然,這個(gè) Context 的實(shí)際類型就是應(yīng)用的 Application。

下面是通過ContentProvider實(shí)現(xiàn)SDK庫初始化的示例代碼:

class ToolContentProvider : ContentProvider() {

    override fun onCreate(): Boolean {
        Log.e(GlobalConfig.LOG_TAG, "ToolContentProvider onCreate")
        AppContextHelper.init(context!!.applicationContext)
        AppContextHelper.initRoomDB(context!!.applicationContext)
        return true
    }

    override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
        return null
    }

    override fun getType(uri: Uri): String? {
        return null
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        return null
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
        return 0
    }

    override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
        return 0
    }
}
<provider
      android:name=".ToolContentProvider"
      android:authorities="${applicationId}.library-tool"
      android:exported="false" />
class MaoApplication : Application() {

    private lateinit var currentActivityRef: WeakReference<Activity>;


    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        Log.e(GlobalConfig.LOG_TAG, "MaoApplication attachBaseContext")
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(GlobalConfig.LOG_TAG, "MaoApplication onCreate")
        initMMKV()
        initCodeView()
	}

    /**
     * 初始化MMKV工具
     */
    private fun initMMKV() {
        Log.e(GlobalConfig.LOG_TAG, "init MMKV")
        MMKV.initialize(this);
    }

    private fun initCodeView() {
        CodeProcessor.init(this)
    }

}

通過ContentProvider實(shí)現(xiàn)SDK庫初始化的功能實(shí)現(xiàn)了,那么 ContentProvider 的 onCreate() 方法是什么時(shí)候被調(diào)用的呢?

下面是日志輸出,來幫助助我們理解初始化時(shí)機(jī):

com.renhui.maomaomedia E/MaoMaoMedia: MaoApplication attachBaseContext
com.renhui.maomaomedia E/MaoMaoMedia: ToolContentProvider onCreate
com.renhui.maomaomedia E/MaoMaoMedia: MaoApplication onCreate

可以看到,它是介于 Application 的 attachBaseContext(Context) 和 onCreate() 之間所調(diào)用的,Application 的 attachBaseContext(Context) 方法被調(diào)用這就意味著 Application 的 Context 被初始化了。這也再次說明我們確實(shí)可以通過ContentProvider來進(jìn)行SDK庫的初始化,并且執(zhí)行時(shí)間在Application的onCreate之前。

二、ContentProvider初始化SDK庫的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  1. 不需要使用SDK庫的開發(fā)者調(diào)用初始化庫的流程,降低了接入成本
  2. 代碼侵入更低,使得SDK庫的代碼隔離性做的更好,而且方便升級和維護(hù)。

缺點(diǎn):

  • 不一定適用SDK庫的使用場景,因?yàn)樵?ContentProvider 的 onCreate() 執(zhí)行在 Application 的 onCreate() 方法之前,倘若你的庫需要有其它業(yè)務(wù)的依賴,那么就不適合這種方式了。
  • 需要注意應(yīng)用安全漏洞問題,避免組件暴露,需要在聲明provider的時(shí)候,配置exported為false。
  • 必須注意Provider的authorities千萬別寫死,否則兩個(gè)引入同樣SDK的App就無法共存了

三、ContentProvider初始化SDK庫實(shí)現(xiàn)的源碼分析

那么為什么在ContentProvider做初始化,能獲取到application context的呢?看一下下面幾段源碼就能知道了。

 private void handleBindApplication(AppBindData data) {
     ....
     final InstrumentationInfo ii;
     ....
     if (ii != null) {
       //1.創(chuàng)建ContentImpl
       final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

        //2.創(chuàng)建Instrumentation
      final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
        ....
        //3.創(chuàng)建Application對象
         Application app;
         app = data.info.makeApplication(data.restrictedBackupMode, null);

         // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);

            mInitialApplication = app;

        ...
        //4.啟動當(dāng)前進(jìn)程中的ContentProvider和調(diào)用其onCreate方法

        if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

        //5.調(diào)用Application的onCreate方法
        try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
    }
 }
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    mNoPerms = testing;

    /*
     * Only allow it to be set once, so after the content service gives
     * this to us clients can't change it.
     */
    if (mContext == null) {
        mContext = context;
        if (context != null) {
            mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService (Context.APP_OPS_SERVICE);
        }
        mMyUid = Process.myUid();
        if (info != null) {
            setReadPermission(info.readPermission);
            setWritePermission(info.writePermission);
            setPathPermissions(info.pathPermissions);
            mExported = info.exported;
            mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
            setAuthorities(info.authority);
        }
        ContentProvider.this.onCreate();
    }
}

可以看到App的啟動過程中加載了provider,并且傳了一個(gè)Application實(shí)例進(jìn)去,最終在ContentProvider中調(diào)用了onCreate()方法。因此,在自定義的ContentProvider中,通過getContext()方法就可以獲取到Application的實(shí)例了。

其實(shí)從這段源碼中,我們也可以看到,ContentProvider中的onCreate()方法是先于Application中的onCreate()方法執(zhí)行的(注意:此時(shí)Application對象已經(jīng)創(chuàng)建)。

四、谷歌的新組件 - App Startup

谷歌推出的App Startup提供了一種在應(yīng)用程序啟動時(shí)高效、直接初始化組件的方法。SDK開發(fā)人員和APP開發(fā)人員都可以使用App Startup簡化啟動順序并顯式設(shè)置初始化順序。App Startup還允許通過定義共享的ContentProvider統(tǒng)一組件的初始化,大大縮短應(yīng)用啟動時(shí)間。

如果項(xiàng)目中的初始化都是同步初始化的話,并且使用到了多個(gè)ContentProvider,App Startup 還是不錯(cuò)的,畢竟統(tǒng)一到了一個(gè)ContentProvider中,同時(shí)支持了簡單的順序依賴。

但是如果在追求App性能與啟動速度的場景中,多個(gè)SDK同時(shí)利用各自定義的ContentProvider實(shí)現(xiàn)“自啟動”, 在各種有先后順序與依賴的SDK初始化下做優(yōu)化,那么 App Startup 就不是很好用了。也正式這個(gè)原因,目前不建議將 App Startup 用于生產(chǎn)環(huán)境中。

目前的推薦方案還是之前我們都使用過的:同步+異步初始化,并通過有向無環(huán)圖拓?fù)渑判虻姆绞絹肀WC內(nèi)部依賴組件的初始化順序。

到此這篇關(guān)于Android使用ContentProvider初始化SDK庫方案總結(jié)的文章就介紹到這了,更多相關(guān)Android初始化SDK庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論