App Startup基本使用及源码分析


介绍

我们在之前的文章《LeakCanary2源码分析》中聊到,LeakCanary2区别于旧版LeadkCanary,无需调用者显式进行初始化,是因为LeakCanary2利用了ContentProvider自动初始化的特性。

而ContentProvider的这种自动初始化的特性,是在应用的启动过程中赋予的,相关代码在ActivityThread.java

private void handleMakeApplication(AppBindData data, ContextImpl appContext) {
	//1. 通过反射创建Application, 并调用 Application.attach()
	app = data.info.makeApplication(data.restrictedBackupMode, null);
 	if (!data.restrictedBackupMode) {
     	if (!ArrayUtils.isEmpty(data.providers)) {
     		2. 初始化ContentProvider
          	installContentProviders(app, data.providers);
     	 }
  	}
   	try {
   		   //3. 调用Application.onCreate()方法
           mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
           ....
        }
}

结合注释,从上面代码我们可以看到,应用程序的创建顺序是:
ContentProvider.onCreate

也就是说,在Application.onCreate之前,ContentProvider已经进行了初始化。利用这个属性,就可以实现类似于LeakCanary2一样的隐式初始化,但是这种方案也有缺点,那就是使用ContentProvider会有额外的耗时,而且无法定义启动顺序。
Cost of Content Provider

从谷歌官方数据来看,一个空的ContentProvider大约会占用2ms的耗时,随着ContentProvider的增加,耗时也会跟着一起增加。如果使用了50个ContentProvider,那么将会占用接近20ms的耗时。对App启动的影响还是比较明显的,针对这种情况,谷歌官方推出了App StartupApp Startup允许共享ContentProvider,将所有用于初始化的ContentProvider合并成一个,并显示定义其依赖关系,从而使App的启动速度变得更快。

基本使用

加入依赖
dependencies {
    implementation "androidx.startup:startup-runtime:1.0.0-alpha03"
}
实现Initializer组件

始化组件必现实现 Initializer 接口,这个接口定义了两个方法:

public interface Initializer<T> {
    @NonNull
    T create(@NonNull Context context);
    
    @NonNull
    List<Class<? extends Initializer<?>>> dependencies();
}

create() :这个方法会包含组件初始话的所有的操作,最终会返回一个实例 T

dependencies():这个方法返回一组实现了 Initializer 的类,这些都是当前组件初始化需要依赖的其他组件。可以使用此方法来控制应用程序在启动时运行初始化程序的顺序

大多数情况下我们不需要依赖其他组件,可以直接返回emptyList(),如下所示:

class BaseInitializer : Initializer<Unit> {
    private val TAG = "BaseInitializer"

    override fun create(context: Context) {
        Log.d(TAG, "create") 
        //在这里进行初始化工作
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}

瞬间有种大费周章写Hello World的感觉~ 接下去需要把BaseInitializer注册到AndroidManifest.xml即可:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.saberhao.jetpacktest_kt.AppStartUp.BaseInitializer"
        tools:node="androidx.startup" />
</provider>

上述配置,只有meta-data中的android:name部分我们需要指定成我们自定义的Initializer的全路径类名,其他部分都是不能修改的。

懒加载

App Startup也提供了懒加载的方式,只需要将meta-datatools:value="androidx.startup"修改为

tools:node="remove",同时在需要初始化的地方手动初始化:

 AppInitializer.getInstance(this).initializeComponent(BaseInitializer::class.java)
依赖

上面我们说过,App Startup可以指定顺序执行,这个功能依赖于方法dependencies(), 如果我们现在新增加LogInitializer, 让LogInitializer依赖于BaseInitializer,在进行LogInitializer初始化前会进行BaseInitializer的初始化工作:

class LogInitializer : Initializer<Unit> {
    val TAG = "LogInitializer"

    override fun create(context: Context) {
        Log.d(TAG, "create")
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return arrayListOf(BaseInitializer::class.java)
    }
}

这个时候,我们可以不用在AndroidManifest.xml中注册BaseInitializer,而只需注册LogInitializer,便可以正常执行:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.saberhao.jetpacktest_kt.AppStartUp.LogInitializer"
        tools:value="androidx.startup" />
</provider>

需要注意的是,禁用组件的自动初始化,也会禁用该组件的依赖项的自动初始化。

源码分析

App Startup 的开始就是 InitializationProvider 的启动,InitializationProvider 继承自ContentProvider

public final class InitializationProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        Context context = getContext();
        if (context != null) {
        	//寻找并初始化各个Initializer
            AppInitializer.getInstance(context).discoverAndInitialize();
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }
    ....
}

我们看看 discoverAndInitialize 的具体实现

void discoverAndInitialize() {
    try {
        Trace.beginSection(SECTION_NAME);
        // 1.获取InitializationProvider在manifest中配置的meta-data
        ComponentName provider = new ComponentName(mContext.getPackageName(),
                InitializationProvider.class.getName());
        ProviderInfo providerInfo = mContext.getPackageManager()
                .getProviderInfo(provider, GET_META_DATA);
        Bundle metadata = providerInfo.metaData;
        String startup = mContext.getString(R.string.androidx_startup);
        if (metadata != null) {
            Set<Class<?>> initializing = new HashSet<>();
            Set<String> keys = metadata.keySet();
            // 2. 遍历meta-data,找到所有metadata为"androidx.startup"的元素
            for (String key : keys) {
                String value = metadata.getString(key, null);
                if (startup.equals(value)) {
                    Class<?> clazz = Class.forName(key);
                    if (Initializer.class.isAssignableFrom(clazz)) {
                        //3. 根据metadata找到所有Initializer组件
                        Class<? extends Initializer<?>> component =
                                (Class<? extends Initializer<?>>) clazz;
                        //4. 对Initializer组件进行初始化
                        doInitialize(component, initializing);
                    }
                }
            }
        }
    } catch (...) {
        throw new StartupException(exception);
    } finally {
        Trace.endSection();
    }
}

discoverAndInitialize通过解析 meta-data ,获得Initializer,并对其进行初始化。

接下去,我们来看看具体对Initializer的初始化流程:

<T> T doInitialize(...) {
    synchronized (sLock) { //加锁保证操作同步
        boolean isTracingEnabled = Trace.isEnabled();
        try {
        	...
            //1. 保证initializer不重复执行
            Object result;
            if (!mInitialized.containsKey(component)) {
                initializing.add(component);
                try {
                	//2. 通过反射创建 initializer
                    Object instance = component.getDeclaredConstructor().newInstance();
                    Initializer<?> initializer = (Initializer<?>) instance;
                    //3. 获取 initializer 的依赖
                    List<Class<? extends Initializer<?>>> dependencies =
                            initializer.dependencies();
				  // 4. 如果依赖不为空,递归初始化依赖
                    if (!dependencies.isEmpty()) {
                        for (Class<? extends Initializer<?>> clazz : dependencies) {
                            if (!mInitialized.containsKey(clazz)) {
                                doInitialize(clazz, initializing);
                            }
                        }
                    }
                    ...
                    // 5. 调用 initializer.create 进行初始化
                    result = initializer.create(mContext);
                    if (StartupLogger.DEBUG) {
                        StartupLogger.i(String.format("Initialized %s", component.getName()));
                    }
                    // 6. 更新正在初始化和已经初始化映射表
                    initializing.remove(component);
                    mInitialized.put(component, result);
                } catch (Throwable throwable) {
                    throw new StartupException(throwable);
                }
            } else {
                result = mInitialized.get(component);
            }
            return (T) result;
        } finally {
            Trace.endSection();
        }
    }
}

代码很简单,根据 meta-data 来获取自定义的initializer,通过反射的方式初始化initializer,递归调用初始化initializer的依赖,然后调用其 create 方法完成最终初始化。

App Startup实现非常简单,应该是我们解析过的最简单的一篇了,希望对大家有帮助~

参考

Jetpack新成员,App Startup一篇就懂
Android Developer - App Startup

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值