Jetpack Startup 优雅完成库的初始化和方法接口简化

1.Startup组件是什么

Startup组件是Google Jetpack推出的,它提供了一种直接高效的方式用来在应用程序启动的时候对多个组件进行初始化,开发者可以依靠它来显示地设置多个组件间的初始化顺序,并优化应用的启动时间。在没有Startup组件之前,很多的三方库为了简化开发者的使用成本,常常会定义一个ContentProvider获取Context的对象,然后自动完成库的初始化,也就是我们之前使用库的时候,总是需要在Application中调用下某某库的:XXXLib.init(Context)方法,主要是将我们的Context传递给三方库。由于ContentProvider会在Application的onCreate()之前调用,所以可以通过定义ContentProvider的方式提前获取到Context。但是这样做会有问题的,因为如果每个三方库都定义一个自己的ContentProvider,并且在拿Context的时候都做了耗时操作,这样就会导致应用的启动变慢和长时间黑屏的问题。因此,Google就推出了Startup组件,Startup组件允许库的开发者和App的开发者共享一个ContentProvider,用于完成各自的初始化逻辑,并且支持设置组件之间的初始化顺序。这样应用就可以选择先初始化需要马上使用到的库,其他的不需要马上使用的库后面再初始化。从而达到缩短应用的启动时间。

2.Startup组件能做啥

2.1 startup组件可以简化用户使用我们提供的库的流程。

比如我们非常熟悉的内存泄漏检测工具LeakCarry,在没有使用Startup组件之前,使用它需要在Application中初始化一下,如下所示:

也就是说用户不仅需要引入依赖,还需要做初始化工作。但是引入了startup组件后,使用LeakCanary就可以只引入依赖了。LeakCanary库会通过startup组件提供的API拿到想要的Context,然后进行内存泄漏的检测工作。下面是LeakCanary库使用startup组件的截图: 首先是Manifest中

初始化类: 然后我们就可以看到在LeakCanary库中就能拿到了Context并进行初始化了。所以我们使用的时候,只需要引入依赖:

kotlin 复制代码
dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}

就可以使用LeakCanary库检测内存泄漏了。

2.2 简化库提供给使用者的API接口

现在的VR眼镜基本上都是基于Android系统的,而3D场景的渲染都依赖于Unity和OpenGL,Unity和OpenGL渲染时的数据有一部分是需要Android系统提供的,所以就需要Android开发接口提供给Unity或者是OpenGL调用。但是很多的时候接口都需要使用单例的方式提供并且需要传递Context,如下所示:

这样调用者在调用的时候就需要使用LoadAllAPIEngine.getInstance(context).getInfo()的方式去使用我们提供的服务,而且还需要调用下LoadAllAPIEngine.getInstance(context).init()方法让我们提供接口的库做一些初始化的动作。在开发的过程中发现使用接口的同事会经常多次调用init()方法,或者是忘记调用release()方法,导致出现一些未知的问题。而且如果是OpenGL调用我们的接口时,往往需要反射获取Android的Context,然后再每次调用接口时传递给提供服务的sdk库。这些都增加了SDK使用者的负担,同时也让我们查问题时显得很吃力。

而利用Startup组件可以可以解决这个问题,使用了startup组件后,使用者只要引入我们的库依赖,就可以使用我们的提供的接口方法获取到想要的信息了,使用startup组件后,调用接口就可以变成。 LoadAllAPIEngine.getInstance().getInfo()了,并且不用调用LoadAllAPIEngine.getInstance(context).init()方法,直接使用接口即可。

3.如何使用Startup组件

3.1 引入依赖

kotlin 复制代码
implementation("androidx.startup:startup-runtime:1.1.1")

3.2 创建一个初始化的类继承Initializer

然后重写create()方法和dependencies()方法。然后在create()方法中就可以拿到引用我们库的APP的Context了。

kotlin 复制代码
class StartupInitializer : Initializer<StartupInitializer>{
    override fun create(context: Context): StartupInitializer = apply {
        val application = context.applicationContext as Application
        application.registerActivityLifecycleCallbacks(object 
        :ActivityLifecycleCallbacks{
            override fun onActivityCreated(activity: Activity, 
            savedInstanceState: Bundle?) {
                FMSdk.getInstance().init(activity)
                Log.d(TAG,"onActivityCreated")
            }

            override fun onActivityStarted(activity: Activity) {

            }

            override fun onActivityResumed(activity: Activity) {
                FMSdk.getInstance().resume()
                Log.d(TAG,"onActivityResumed")

            }

            override fun onActivityPaused(activity: Activity) {

            }

            override fun onActivityStopped(activity: Activity) {
                FMSdk.getInstance().pause()
                Log.d(TAG,"onActivityStopped")
            }

            override fun onActivitySaveInstanceState(activity: Activity, 
            outState: Bundle) {

            }

            override fun onActivityDestroyed(activity: Activity) {
                FMSdk.getInstance().release()
                Log.d(TAG,"onActivityDestroyed")
            }

        })
    }

    //获取在初始化自身之前需要先初始化的其他Initializer 列表 ,
    // 这里有助于我们管理库的依赖顺序,比如我们的库依赖其他的库A,并且需要在
    // A之后进行初始化,就可以在这里配置
    //如果不需要依赖于其它组件,则可以返回一个空列表
    override fun dependencies()  = emptyList<Class<out Initializer<*>>>()
}

3.3 在我们库的AndroidManifest.xml中加入配置

定义好初始化的类后,我们需要在我们的库项目中声明一个provider,配置authorities为引用我们库的app的包名+".androidx-startup",然后定义一个<meta-data ../>节点,将我们定义的初始化类引用配置上去,这里我定义的类引用为:com.nolovr.core.fmsdk.engine.StartupInitializer。具体配置参考下面代码。

kotlin 复制代码
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <meta-data
            android:name="com.nolovr.core.fmsdk.engine.StartupInitializer"
            android:value="androidx.startup"
            />
    </provider>    
</application>

上面的步骤配置完后,开发者接入我们的库时就可以直接引入依赖,然后直接调用我们的库中的对应方法,不用再去Application下去做啥初始化操作啦

4.使用Startup组件的注意点

(1)使用Startup我们需要注意不能在StartupInitializer 的create()方法中做耗时操作,因为create方法是在主线程被调用的,如果做耗时操作会导致初始化时间变长,导致应用出现启动慢,黑白屏的问题。 (2)在引用库的主项目中不能使用tools:node="remove" 的语句要求在合并 AndroidManifest 文件时移除自身否则自动初始化的功能就无法使用了。例如不能在主项目中做如下配置:

kotlin 复制代码
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.nolovr.core.fmsdk.engine.StartupInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>

或者是:

kotlin 复制代码
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove" />

因为这样会导致我们的startup组件不生效,当然,如果是想自己手动初始化库的情况下除外。

相关推荐
x02414 天前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton17 天前
深入理解观察者模式
android·kotlin·android jetpack
Wgllss17 天前
花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
android·性能优化·android jetpack
上官阳阳20 天前
使用Compose创造有趣的动画:使用Compose共享元素
android·android jetpack
沐言人生24 天前
Android10 Framework—Init进程-15.属性变化控制Service
android·android studio·android jetpack
IAM四十二1 个月前
Android Jetpack Core
android·android studio·android jetpack
王能1 个月前
Kotlin真·全平台——Kotlin Compose Multiplatform Mobile(kotlin跨平台方案、KMP、KMM)
android·ios·kotlin·web·android jetpack·kmp·kmm
alexhilton1 个月前
让Activity更加优雅地跳转
android·kotlin·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-11.客户端操作属性
android·android studio·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack