• Jetpack架构组件学习(4)——APP Startup库的使用


    本文为作者原创,允许转载,不过请在文章开头明显处注明链接和出处!!! 谢谢配合~
    作者:stars-one
    链接:https://www.cnblogs.com/stars-one/p/16635750.html

    本篇大约有6629个字,阅读预计需要8.29分钟


    最近在研究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(): Listout Initializer<*>>> {
            return emptyList()
        }
    }
    

    3.声明provider

    AndroidManifest.xml文件中声明:

    
    <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(): Listout 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(): Listout 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

    参考

  • 相关阅读:
    C++之继承<2>【详解】
    Linux账号管理:用户账号与用户组
    信号量(Semaphore)
    CAUSE: 加载证书时出错PKCS12 key store mac invalid - wrong password
    正则表达式学习笔记
    文本截取内容易语言代码
    U-net网络学习记录
    互联网Java工程师面试题·MyBatis 篇·第二弹
    IDEA 2023搭建 SpringMVC +FreeMarker+JDBC
    【译】在 Visual Studio 2022 中安全地在 HTTP 请求中使用机密
  • 原文地址:https://www.cnblogs.com/stars-one/p/16635750.html