一、问题复现
近期做项目,发现导入了一些第三方库后,应用的启动时间明显延长了,一番捣鼓之后,也是发现了问题所在,在此将解决方法记录下来
在此我也将问题复现下,我们新建个项目,然后直接运行安装到Android设备上,安装成功后,我们需要退出,并将后台给清掉,因为我们需要测试的是应用的冷启动时间
初始启动时间
接着我们调用adb命令去查看应用冷启动的时间:
scss
adb shell am start -W (应用包名)/MainActivity
可以看到耗时为

添加第三方库之后的启动时间
这里我们以添加utilcode工具库为例

之后同样是调用adb命令去查看应用的冷启动时间

可以看到应用的启动时间变慢了,我之前这里好奇的是,我只是添加了库,没对库进行初始化,也还没用到它,应用怎么就会变慢了呢?这里我们去通过对应用的的AndroidManifest去进行分析
打开AndroidManifest.xml文件,点击Merged Manifest

通过这里我们可以看到文件里多了个provider

点进去查看发现跳转到了第三方库的AndroidManifest.xml文件

再点进去,可以发现第三方库在此去进行了初始化操作,至此可以发现导致应用启动慢的罪魁祸首就在这里
通过查Android开发者指南可以看到第三方库会在应用启动的时候去进行初始化

二、手动初始化
2.1 移除provider
既然第三方库的自动初始化会导致应用变慢,那就要由我们自己去决定第三方库何时初始化
这就要请到我们今天的主角了------Jetpack之Startup库,现在最新的地址如下:
arduino
implementation 'androidx.startup:startup-runtime:1.1.1'
在AndroidManifest.xml中填写如下代码:
xml
<!--表示将第三方库utilcode的provider移除掉-->
<provider
android:name="com.blankj.utilcode.util.UtilsFileProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />
<!--表示将第三方库startup的provider移除掉,因为我们要自己手动去初始化-->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

再次点击Merged Manifest可以发现provider没有了

2.2 定义组件初始化程序
创建一个实现 Initializer<T>
接口的类
create()
方法,其中包含初始化组件并返回T
实例的所有必要操作。dependencies()
方法,该方法返回初始化程序所依赖的其他Initializer<T>
对象的列表。您可以使用此方法控制应用在启动时的顺序。
对原先的provider初始化去进行观察

可以发现返回一个boolean值,并且直接返回true
这里我们创建一个UtilCodeInitializer
类,并实现Initializer<T>
,因为上面的是返回一个boolean值,所以这里的T为Boolean
kotlin
class UtilCodeInitializer:Initializer<Boolean> {
override fun create(context: Context): Boolean {
Utils.init(context.applicationContext as Application)
return true
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
这个类也就代替了provider的初始化功能
2.3延迟初始化
应用原本是一启动第三方库就会初始化,这里我们把它的初始化放到onCreate中执行,并且延迟两秒
为了实现效果的更明显,我这里又添加了一个自定义的provider,并在里面去延迟500ms
scala
public class TestProvider extends FileProvider {
@Override
public boolean onCreate() {
//noinspection ConstantConditions
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return true;
}
}
并在AndroidManifest.xml中去注册
ini
<provider
android:name=".TestProvider"
android:authorities="${applicationId}.example"
android:grantUriPermissions="true"
android:exported="false" />
自动初始化时间

这里的第三方库和我们自定义的provider会在应用启动的时候去初始化

可以看到应用启动时间为800多ms,运行的时候明显感觉慢了一些
延迟初始化
这里我们同样对TestProvider去实现初始化
kotlin
class TestProviderInitializer:Initializer<Boolean> {
override fun create(context: Context): Boolean {
try {
Thread.sleep(500)
} catch (e: InterruptedException) {
throw RuntimeException(e)
}
return true
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
对AndroidManifest.xml中的三个Provider标记去除

在MainActivity的onCreate中去进行延迟2s初始化

ini
//这几行代码的作用就代替了之前provider里面的初始化
AppInitializer initializer = AppInitializer.getInstance(MainActivity.this);
initializer.initializeComponent(UtilCodeInitializer.class);
initializer.initializeComponent(TestProviderInitializer.class);
可以看到时间明显缩短了,查看Merged Manifest也是一个provider也没有了


三、总结
这篇文章可能会有点啰嗦,但核心思路是有些第三方库中会注册provider在里面去进行初始化,从而导致拖慢应用的启动,当第三方库非常多的时候,拖慢的会更明显,我们要做的就是将第三方库的provider从AndroidManifest.xml文件中移除,并且实现一个类去接管它的初始化,初始化的时机由我们来定,可以是需要的时候再初始化,也可以延迟一段时间初始化,除了这个功能之外,Startup还有许多的功能,这里我就不再赘述了,有需要的看下官方文档就可以了,应用启动 | Android 开发者 | Android Developers (google.cn),非常感谢大家看到这里!