Android组件初始化三种方式小结
作者:拉不拉了个多
在Android中,组件的初始化通常需要在适当的生命周期方法中进行,本文给大家详细的介绍了初始化的三种方式,文中有详细的代码示例供大家参考,具有一定的参考价值,需要的朋友可以参考下
方式一:使用 Application#onCreate 进行初始化
使用方式
- 自定义
CustomApplication
class CustomApplication : Application() { // .. override fun onCreate() { super.onCreate() // 进行组件初始化 } // .. }
- 在
主module
清单文件中声明使用CustomApplication
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- ... --> <application android:name="CustomApplication全路径或者相对于资源的路径"> <!-- ... --> </application> </manifest>
优缺点
- 优点:简单易用,只需在
主module
的Application#onCreate
进行组件初始化,并且可以指定组件间初始化顺序; - 缺点:在其他
主module
使用的时候,需要在自身Application#onCreate
进行一遍初始化(对应组件依赖方来说,较为繁琐);同时如果组件存在依赖关系,使用方要清楚组件之间的关系,从而确定组件初始化顺序,增加组件使用成本;
方式二:使用 Content Provider 进行初始化
使用方式
- 自定义
ContentProvider
class CustomProvider : ContentProvider() { override fun onCreate(): Boolean { // 初始化组件 return true } override fun query( uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? = null override fun getType(uri: Uri): String? = null override fun insert(uri: Uri, values: ContentValues?): Uri? = null override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0 override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>? ): Int = 0 }
- 自定义
ContentProvider
在当前组件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"> <!-- ... --> <application> <!-- 1. name 为 CustomProvider 的全路径,或者相对于资源文件的路径 2. authorities 加上 ${applicationId} 可以有效避免多个应用依赖该组件重复,导致不能安装问题 3. exported = false,让外部不能使用,纯粹只是为了当前组件的初始化 --> <provider android:name="CustomProvider的路径" android:authorities="${applicationId}.custom-startup" android:exported="false" /> <!-- ... --> </application> </manifest>
优缺点
- 优点:在应用启动的时候,系统会调用
ContentProvider#onCreate
进行初始化,则避免组件使用方在主module
中Application#onCreate
初始化,对于组件使用方来说是无感知的; - 缺点:如果组件间初始化是有依赖性,则多个
Provider
在清单文件中的顺序是有要求的,但这个是非常困难进行调整的,并且很容易错;同时Provider
的实例化成本高昂,在不必要的情况下可能会拖慢启动序列
ContentProvider#onCreate 初始化路径(基于android33)
- ActivityThread#main(
zygote
新建进程,执行ActivityThread
的main
方法,执行主Looper
循环) - ActivityThread#attach(
ActivityThread
初始化) - ActivityManagerService#attachApplication(
Binder
调用,从应用 =>ActivityManagerService
) - ApplicationThread#bindApplication(
Binder
调用,从ActivityManagerService
=> 应用) - H#handleMessage(BIND_APPLICATION)(使用
H
进行分发) - ActivityThread#handleBindApplication(当前应用绑定
application
) - ActivityThread#installContentProviders(安装多个
Provider
) - ActivityThread#installProvider(安装单个
Provider
) - ContentProvider#attachInfo
- ContentProvider#onCreate
方式三:使用 Jetpack startup 组件库进行初始化
使用方式
- 在
module
模块的build.gradle
引用Jetpack startup
dependencies { implementation "androidx.startup:startup-runtime:1.1.1" }
- 继承
Initializer<*>
,重写onCreate
和dependencies
方法
class CustomInitializer : Initializer<Custom> { override fun create(context: Context): Custom { // Custom 初始化 return Custom.getInstance(context) } override fun dependencies(): List<Class<out Initializer<*>>> { // 声明当前依赖的类库 return emptyList() } }
- 在模块
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"> <!-- ... --> <application> <!-- 1. name 为固定 androidx.startup.InitializationProvider,则知道是用这个来进行初始化的 2. authorities = "${applicationId}.androidx-startup" 用于避免多个应用导致的重复 3. exported 仅供自身使用,不对外暴露 4. tools:node = "merge" 表示合并相同的 InitializationProvider,在这里代表合并其他组件的 meta-data --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- 1. name 为 CustomInitializer 的全路径,或者相对于资源文件的路径 2. value 为固定的 androidx.startup,用于提取 CustomInitializer 的路径 --> <meta-data android:name="CustomInitializer的路径" android:value="androidx.startup" /> </provider> <!-- ... --> </application> </manifest>
优缺点
- 优点:解决方法二中的2个问题;在整体应用中,只存在一个
Provider
,并且Initializer#dependencies
中声明该组件所依赖组件,在初始该组件的时候会先初始化该组件所依赖组件 - 缺点:从
Initializer#dependencies
方法参数可知,需要所依赖组件实现Initializer
才可以,需要改动原来的组件;但如果后续的组件都按照此来声明,对于使用方来说,会更加简单
源码解析
- 从
android:name
可知InitializationProvider
入口
// androidx.startup.InitializationProvider public class InitializationProvider extends ContentProvider { @Override public final boolean onCreate() { Context context = getContext(); if (context != null) { Context applicationContext = context.getApplicationContext(); if (applicationContext != null) { // 调用 AppInitializer 的 discoverAndInitialize AppInitializer.getInstance(context).discoverAndInitialize(); } else { StartupLogger.w("Deferring initialization because `applicationContext` is null."); } } else { throw new StartupException("Context cannot be null"); } return true; } // ... }
AppInitializer#discoverAndInitialize
public final class AppInitializer { private static volatile AppInitializer sInstance; private static final Object sLock = new Object(); final Map<Class<?>, Object> mInitialized; final Set<Class<? extends Initializer<?>>> mDiscovered; final Context mContext; @NonNull public static AppInitializer getInstance(@NonNull Context context) { // 双重校验获取 AppInitializer if (sInstance == null) { synchronized (sLock) { if (sInstance == null) { sInstance = new AppInitializer(context); } } } return sInstance; } AppInitializer(@NonNull Context context) { mContext = context.getApplicationContext(); // application context mDiscovered = new HashSet<>(); // 已发现的Initializer mInitialized = new HashMap<>(); // 已初始化的内容 } void discoverAndInitialize() { try { // step1: 创建包含 InitializationProvider 类信息的 ComponentName ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName()); // step2: 获取 InitializationProvider 对应的 META_DATA 信息 ProviderInfo providerInfo = mContext.getPackageManager() .getProviderInfo(provider, GET_META_DATA); Bundle metadata = providerInfo.metaData; // step3: 解析metadata discoverAndInitialize(metadata); } catch (PackageManager.NameNotFoundException exception) { throw new StartupException(exception); } } void discoverAndInitialize(@Nullable Bundle metadata) { // step4: 获取 startup 字符串 String startup = mContext.getString(R.string.androidx_startup); try { if (metadata != null) { // step5: initializing 记录正在初始化的类,主要用于防止循环引用 Set<Class<?>> initializing = new HashSet<>(); Set<String> keys = metadata.keySet(); for (String key : keys) { String value = metadata.getString(key, null); // step6: 这里限定了 android:value 只能为 startup 的值 if (startup.equals(value)) { // step7: 反射获取此类,并检查是否是 Initializer 的子类 Class<?> clazz = Class.forName(key); if (Initializer.class.isAssignableFrom(clazz)) { Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz; // step8: 记录当前类进入发现集合中 mDiscovered.add(component); } } } // step9: 遍历当前发现的类,开始初始化 for (Class<? extends Initializer<?>> component : mDiscovered) { // Tips: initializing 记录正在初始化的类,这里为空集合 doInitialize(component, initializing); } } } catch (ClassNotFoundException exception) { throw new StartupException(exception); } } @NonNull @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) private <T> T doInitialize( @NonNull Class<? extends Initializer<?>> component, @NonNull Set<Class<?>> initializing) { try { // step10: 如果 initializing 包含 component,则证明出现循环依赖 if (initializing.contains(component)) { String message = String.format( "Cannot initialize %s. Cycle detected.", component.getName() ); throw new IllegalStateException(message); } Object result; // step11: 检查此类是否已初始化,已初始化这直接返回,反之进行初始化 if (!mInitialized.containsKey(component)) { initializing.add(component); try { // step12: 使用反射调用构造参数;由这可知这里需要一个默认的空参的构造函数 Object instance = component.getDeclaredConstructor().newInstance(); Initializer<?> initializer = (Initializer<?>) instance; List<Class<? extends Initializer<?>>> dependencies = initializer.dependencies(); if (!dependencies.isEmpty()) { for (Class<? extends Initializer<?>> clazz : dependencies) { // step13: 如果当前依赖组件未进行初始化,则进行初始化 if (!mInitialized.containsKey(clazz)) { doInitialize(clazz, initializing); } } } // step14: 调用 Initializer#create 创建组件 result = initializer.create(mContext); // step15: 当前组件初始化完成,从正在初始化集合移除和加入已初始化集合中 initializing.remove(component); mInitialized.put(component, result); } catch (Throwable throwable) { throw new StartupException(throwable); } } else { result = mInitialized.get(component); } return (T) result; } } // ======================= 剩余方法 ======================= @NonNull @SuppressWarnings("unused") public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) { return doInitialize(component); } public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) { return mDiscovered.contains(component); } @NonNull @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) <T> T doInitialize(@NonNull Class<? extends Initializer<?>> component) { Object result; synchronized (sLock) { result = mInitialized.get(component); if (result == null) { result = doInitialize(component, new HashSet<Class<?>>()); } } return (T) result; } }
Tips
从上述的源码解析可知,整个过程分为2部分
- 提取当前
Provider
的meta-data
数据,从meta-data
上可以获取对应Initializer
的类信息 - 根据提取到
Initializer
的类信息,进行反射调用构造函数和调用onCreate
方法;
因此可以不使用第一步进行初始化,选择合适时机进行初始化,也就官网说的延迟初始化, 此时调用上述
AppInitializer#initializeComponent
进行初始化
默认行为是从 Provider
清单文件声明 meta-data
提取类信息,因此当不需要某个初始化的时候,可以屏蔽对应 meta-data
的类信息;或者使用 aapt2
所带的 tools:node="remove"
进行移除
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- tools:node="remove" 在构建的时候,不会打进去 --> <meta-data android:name="com.example.ExampleLoggerInitializer" tools:node="remove" /> </provider>
同时 tools:node="remove"
也适用于 provider
结点,使整个 provider
结点移除
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove" />
以上就是Android组件初始化三种方式小结的详细内容,更多关于Android组件初始化的资料请关注脚本之家其它相关文章!