7

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

 2 years ago
source link: https://www.cnblogs.com/stars-one/p/16635750.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文为作者原创,转载请注明出处,谢谢配合
作者:stars-one
链接:https://www.cnblogs.com/stars-one/p/16635750.html

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


最近在研究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}作为前缀,以防止和其他应用程序冲突。

运行结果如下所示:

1210268-20220831220056401-2106616061.png

关于运行的流程,如下图所示

1210268-20220831220358098-2016995291.png

一个应用程序的执行顺序是这个样子的。首先调用ApplicationattachBaseContext()方法,

然后调用ContentProvideronCreate()方法,接下来调用Application的onCreate()方法。

我们使用上述方法治好,在提供库给别的开发者用的时候,别的开发者就不用再去多写一步初始化的操作了,简化了其他开发者使用库的操作流程。

上面的方法虽然是比较巧妙,但是ContentProvider会增加许多额外的耗时

1210268-20220831220622394-1125210540.png

一个空白的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本质上也是通过反射来找到你的这个类)

效果如下图所示:

1210268-20220831223531142-695187421.png

进阶使用(依赖关系)

我们再添加一个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的初始化类,本来我以为声明的顺序会影响初始化的顺序,实际上并不影响

以下截图就是上述代码运行的效果:

1210268-20220831224633910-789743198.png

于是,我们现在要将上面的顺序变更为以下:

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)
    }
}

运行结果:

1210268-20220831224829275-216739377.png

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK