美文网首页基础知识Android收藏集
Jetpack 之 App Startup 源码分析

Jetpack 之 App Startup 源码分析

作者: 你怕是很皮哦 | 来源:发表于2020-06-15 16:31 被阅读0次

    抛砖引玉

    使用过 LeakCanary 的童鞋都知道,早在 LeakCanary 1.x 版本的时候,我们需要在 Application 中手动调用 install 方法来完成 LeakCanary 的初始化过程。

    LeakCanary 2.x 的版本实现了自动初始化。通过查看 LeakCanary 2.x 的源码,我们可以发现,它通过继承 ContentProvider ,在 onCreate() 中来完成初始化过程。

    internal sealed class AppWatcherInstaller : ContentProvider() {
    
      override fun onCreate(): Boolean {
        val application = context!!.applicationContext as Application
    
        val leakCanaryEnable = application
                .getSharedPreferences(KEY_LEAK_CANARY_ENABLE_FILE, Context.MODE_PRIVATE)
                .getBoolean(KEY_LEAK_CANARY_ENABLE, false)
        if (!leakCanaryEnable) {
          Log.d("LeakCanary", "Sorry ! you can't init LeakCanary")
          return true
        }
        InternalAppWatcher.install(application)
        return true
      }
      
    }
    

    为什么使用 ContentProvider 就能够达到自动初始化呢?

    在应用程序启动过程中,会调用到 ActivityThread.handleBindApplication(),我们可以看到如下关键代码。

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

    通过源码,我们知道它的调用顺序如下。

    1. 创建 Application,调用 Application.attach()
    2. 装载 ContentProvider
    3. 调用 Application.onCreate();

    所以在 ContentProvider.onCreate() 里面是可以拿到 application context , 在这里进行初始化是没有问题的。

    而 本文介绍的 Jetpack 的新成员 App Startup 就是通过 ContentProvider 来完成实现的。

    App Startup 的使用

    使用 App Startup 库,我们需要在 build.gradle 中添加依赖。

    implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
    

    实现 Initializer 接口,为组件创建初始化程序。

    举个栗子,这里为 LibraryA 创建初始化程序,dependencies() 返回了一个集合,表明它依赖于其他库的初始化。

    class LibraryAInitializer : Initializer<LibraryA> {
    
        override fun create(context: Context): LibraryA {
            return LibraryA.getInstance(context)
        }
    
        override fun dependencies(): MutableList<Class<out Initializer<*>>> {
            return arrayListOf()
        }
    
    }
    

    manifest.xml 文件中配置 InitializationProvider, App Startup 使用指定的 ContentProvider 来检索和初始化 Initializer

    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
    </provider>
    

    每个 Initializer 使用 <meta-data /> 节点定义,其 value 必须为 androidx.startup(这里后面源码会讲到)。

    <meta-data
        android:name="com.ppdai.code.jetpack.startup.initializer.LibraryAInitializer"
        android:value="androidx.startup" />
    

    这样,在 app 启动过程中就会自动初始化 LibraryA 这个库,是不是非常的简单。

    假设 LibraryA 的初始化依赖于 LibraryB,我们只需要编写 LibraryB 的 Initializer,然后修改 LibraryAInitializer,在其 dependencies() 方法中返回 LibraryB 的 Initializer 即可。

    class LibraryAInitializer : Initializer<LibraryA> {
    
        override fun create(context: Context): LibraryA {
            return LibraryA.getInstance(context)
        }
    
        override fun dependencies(): MutableList<Class<out Initializer<*>>> {
            return arrayListOf(LibraryBInitializer::class.java)
        }
    
    }
    
    class LibraryBInitializer : Initializer<LibraryB> {
    
        override fun create(context: Context): LibraryB {
            return LibraryB.getInstance(context)
        }
    
        override fun dependencies(): MutableList<Class<out Initializer<*>>> {
            return arrayListOf()
        }
    
    }
    

    这样在初始化 LibraryA 的时候会先初始化 LibraryB。

    如果不希望在 app 启动的时候初始化,我们也可以通过 AppInitializer.initializeComponent() 在代码中对指定的组件进行初始化,如下所示。

    AppInitializer.getInstance(this).initializeComponent(LibraryAInitializer::class.java)
    

    源码分析

    InitializationProvider

    InitializationProviderApplication.onCreate() 调用之前检索和初始化 Initializer

    我们来看看 InitializationProvider 的具体实现,可以发现它只是在 onCreate() 方法中调用了 AppInitializerdiscoverAndInitialize()

    public final class InitializationProvider extends ContentProvider {
        @Override
        public boolean onCreate() {
            Context context = getContext();
            if (context != null) {
                AppInitializer.getInstance(context).discoverAndInitialize();
            } else {
                throw new StartupException("Context cannot be null");
            }
            return true;
        }
    
        @Nullable
        @Override
        public Cursor query(
                @NonNull Uri uri,
                @Nullable String[] projection,
                @Nullable String selection,
                @Nullable String[] selectionArgs,
                @Nullable String sortOrder) {
            throw new IllegalStateException("Not allowed.");
        }
    
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            throw new IllegalStateException("Not allowed.");
        }
    
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
            throw new IllegalStateException("Not allowed.");
        }
    
        @Override
        public int delete(
                @NonNull Uri uri,
                @Nullable String selection,
                @Nullable String[] selectionArgs) {
            throw new IllegalStateException("Not allowed.");
        }
    
        @Override
        public int update(
                @NonNull Uri uri,
                @Nullable ContentValues values,
                @Nullable String selection,
                @Nullable String[] selectionArgs) {
            throw new IllegalStateException("Not allowed.");
        }
    }
    

    AppInitializer

    AppInitializer 是真正对 Initializer 进行初始化的地方。

    AppInitializer 可以拆分为三个部分。

    1. AppInitializer 的创建;
    2. 如何初始化在 manifest.xml 中配置的 Initializer
    3. 如何在代码中初始化指定 Initializer

    创建

    AppInitializer 使用单例模式。成员变量 mInitialized 用来存放已经初始化的 Initializer,保证所有的 Intializer 只被初始化一次。

    private static AppInitializer sInstance;
    
    // 用来存放已初始化的 Initializer
    @NonNull
    final Map<Class<?>, Object> mInitialized;
    
    @NonNull
    final Context mContext;
    
    AppInitializer(@NonNull Context context) {
        mContext = context.getApplicationContext();
        mInitialized = new HashMap<>();
    }
    
    @NonNull
    @SuppressWarnings("UnusedReturnValue")
    public static AppInitializer getInstance(@NonNull Context context) {
        synchronized (sLock) {
            if (sInstance == null) {
                sInstance = new AppInitializer(context);
            }
            return sInstance;
        }
    }
    

    初始化 manifest.xml 中配置的 Initializer

    这个过程主要是通过 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;
            // androidx.startup
            String startup = mContext.getString(R.string.androidx_startup);
            if (metadata != null) {
                Set<Class<?>> initializing = new HashSet<>();
                Set<String> keys = metadata.keySet();
                // 2. 遍历meta-data,找到所有value="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)) {
                            Class<? extends Initializer<?>> component =
                                    (Class<? extends Initializer<?>>) clazz;
                            if (StartupLogger.DEBUG) {
                                StartupLogger.i(String.format("Discovered %s", key));
                            }
                            // 3. 初始化指定的 Initializer
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }
    

    可以看出,通过解析 meta-data ,我们就能拿到配置的 Initializer,然后逐一调用 doInitialize() 进行初始化。

    再看看 doInitialize 做了什么事情。

    <T> T doInitialize(
            @NonNull Class<? extends Initializer<?>> component,
            @NonNull Set<Class<?>> initializing) {
        synchronized (sLock) {
            boolean isTracingEnabled = Trace.isEnabled();
            try {
                if (isTracingEnabled) {
                    // Use the simpleName here because section names would get too big otherwise.
                    Trace.beginSection(component.getSimpleName());
                }
                if (initializing.contains(component)) {
                    String message = String.format(
                            "Cannot initialize %s. Cycle detected.", component.getName()
                    );
                    throw new IllegalStateException(message);
                }
                Object result;
                // 1. 保证 Initializer 只初始化一次
                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. 递归初始化,先初始化 Initializer 依赖的且没有被初始化过的 Initializer
                        if (!dependencies.isEmpty()) {
                            for (Class<? extends Initializer<?>> clazz : dependencies) {
                                if (!mInitialized.containsKey(clazz)) {
                                    doInitialize(clazz, initializing);
                                }
                            }
                        }
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }
                        // 5. 初始化自身
                        result = initializer.create(mContext);
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }
                        initializing.remove(component);
                        // 6. 将 Initializer 放入已初始化 Map
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw new StartupException(throwable);
                    }
                } else {
                    result = mInitialized.get(component);
                }
                return (T) result;
            } finally {
                Trace.endSection();
            }
        }
    }
    

    在代码中初始化指定的Initializer

    我们可以通过 AppInitializerinitializeComponent() 在代码中初始化指定的 Initializer

    public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
        return doInitialize(component, new HashSet<Class<?>>());
    }
    

    可以看到,它实际上也是调用了 doInitialize() 方法。

    Initializer

    用来在 app startup 过程中初始化 libraries

    public interface Initializer<T> {
    
        // application context
        @NonNull
        T create(@NonNull Context context);
    
        // 返回 Initializer 的依赖。比如初始化 A,A 依赖于B,那么在初始化 A 的时候会优先查看 B 是否初始化了,如果没有初始化 B,则会先初始化 B。
        @NonNull
        List<Class<? extends Initializer<?>>> dependencies();
    }
    

    参考

    App Startup

    Github/Jetpack-App Startup

    相关文章

      网友评论

        本文标题:Jetpack 之 App Startup 源码分析

        本文链接:https://www.haomeiwen.com/subject/nvkhxktx.html