【绝非标题党】jetpack之startUp组件原理解析



Jetpack StartUp官网 需要翻墙

StartUp的作用 > 如果一个app依赖了很多需要初始化的sdk, 对于开发者来说,都是放在application启动的时候oncreate里面去初始化,这样是不太友好的。而StartUp就能解决这个问题。多人协作开发的时候,都需要去修改application里面的代码,这个是比较容易出问题的。 所以startUp这个库,有一种模块化的思想在里面。

StartUp的原理 > 借助于ContentProvider. 相信做android开发的朋友,都应该知道App在启动的时候会去解析清单文章里面注册的ContentProvider, 然后调用了ContentProvider里面的onCreate生命周期方法, 因此, 整个app的初始化代码的入口就 可以 放在contentProvicer的onCreate方法里面执行。

ContentProvider需要我们自己去写吗 > ContentProvider当然不需要我们自己去写,这个已经在SDK里面已经提供好了。 只需要引入SDK即可

arduino 复制代码
dependencies {
    implementation "androidx.startup:startup-runtime:1.1.1"
}

注册ContentProvider > ContentProvider是属于四大组件之一, 因此必须要注册到清单文件里面。 而注册到清单文件里面的代码,只需要根据官网提供的文档注册即可。对于 下面的代码,对于开发者而言,只需要修改meta-data 里面的类的路径,其它 的代码都是不需要改动的。

xml 复制代码
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- This entry makes ExampleLoggerInitializer discoverable. -->
    <meta-data  android:name="com.example.ExampleLoggerInitializer"
          android:value="androidx.startup" />
</provider>

meta-data具体有什么用 > 前面已经说过,ContentProvider启动的时候,就会去执行初始化代码。 而这初始化代码哪里来? 就是根据metaData里面提供的类的路径,反射 创建出实例,从而执行对应的方法。 > > 而meta-data里面配置的类并不是任意一个类都是可以的, 而是要实现SDK 里面提供的一个接口。 并重写对应的两个方法

less 复制代码
public interface Initializer<T> {

  @NonNull
  T create(@NonNull Context context);

  @NonNull
  List<Class<? extends Initializer<?>>> dependencies();
}

接口方法介绍 > 接口需要传入一个泛型, 而create方法需要返回泛型的实例。 首先看一下 create()方法的作用: 初始化的代码 就是在create方法里面执行的。 > > 需要一个泛型, 那就创建一个类,这个类的名字是可以随便定义的,比如我创建 一个UmengInit的类,这个类对外暴露一个init方法。而这个UmengInit类,才是真正的 代码初始化的类,而暴露的这个init方法就是需要在上面的oncreate方法里执行的。 代码如下:

kotlin 复制代码
class UmengInit {
    fun init(application: Context?) {
        umengInit(application)
        pushInit(application)
    }

    private fun umengInit(application: Context?) {
        val umengKey = ResourceUtils.getMetaDateFromName("UMENG_APPKEY")
        val umengMessageScret = ResourceUtils.getMetaDateFromName("UMENG_MESSAGE_SCRET")
        val channel = PackerNg.getChannel(application)
        UMConfigure.init(
            application,
            umengKey,
            channel,
            UMConfigure.DEVICE_TYPE_PHONE,
            umengMessageScret
        )
        UmengConfig.setId()
    }

    private fun pushInit(application: Context?) {
        val push = UmengPushAdapter()
        push?.init(application)
    }
}

在onCreate方法里面如何使用UmengInit类 > 创建一个类,必须要实现上面的Initializer<T>接口, 这里的泛型就是UmengInit 而此时创建的这个类就是需要添加到清单文件meta-data里面 只需要把name改成刚创建的类的全路径即可。 比如我创建的这个类的代码如下:

less 复制代码
public class UmengInitial implements Initializer<UmengInit> {
    @NonNull
    @Override
    public UmengInit create(@NonNull Context context) {
        UmengInit init = new UmengInit();
        init.init(context);
        return init;
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        List<Class<? extends Initializer<?>>> list = new ArrayList();
        list.add(FirstInitial.class);
        return list;
    }
}

结束语 > 写到这里面,可以说startUp的初始化的脉络已经讲得差不多了,这个时候运行代码,这个初始化 代码是肯定可以的执行的。

你确定你讲清楚了吗? dependencies()方法不都还没讲吗? 哦!!! 头脑晕了,确实没讲。

dependencies()方法又有何用呢 > 上面的UmengInitial只是负责初始化umenng, 功能是单一的, 这个时候如果有一个图片初始化,比如叫: ImageInitial. 如何在功能上需要 ImageInitial的初始化代码要先于UmengInitial的初始化代码,那该如何实现呢? > > > 有两种方案: > > 第一种方案,用到清单文章里面meta-data注册的先后顺序

> 第二种方案,用到dependencies()方法

第一种方案, ImageInitial写在前面就能保证先执行

ini 复制代码
 <provider
                android:authorities="${applicationId}.androidx-startup"
                android:name="androidx.startup.InitializationProvider"
                android:exported="false"
                tools:node="merge">
            <meta-data
                    android:name="com.micker.aqhy.application.initial.ImageInitial"
                    android:value="@string/androidx_startup"/>
            <meta-data
                    android:name="com.micker.aqhy.application.initial.UmengInitial"
                    android:value="@string/androidx_startup"/>
        </provider>

第二种方案 > 在dependencies()方法里面把ImageInitial添加进去 在这个方法里面关联的Initializer,是要优先执行的。 而且这样写了之后,在清单文件里面就不用注册ImageInitial, 只需要注册UmengInitial就行

less 复制代码
public class UmengInitial implements Initializer<UmengInit> {
    @NonNull
    @Override
    public UmengInit create(@NonNull Context context) {
        UmengInit init = new UmengInit();
        init.init(context);
        return init;
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        List<Class<? extends Initializer<?>>> list = new ArrayList();
        list.add(ImageInitial.class);
        return list;
    }
}

最后我把源码给贴上来, 源码很简单, 相信你都能看明白

ini 复制代码
 void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);
            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();
                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));
                            }
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }
less 复制代码
 <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;
                if (!mInitialized.containsKey(component)) {
                    initializing.add(component);
                    try {
                        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) {
                                if (!mInitialized.containsKey(clazz)) {
                                    doInitialize(clazz, initializing);
                                }
                            }
                        }
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }
                        result = initializer.create(mContext);
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }
                        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();
            }
        }
    }
相关推荐
雨白9 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk9 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING9 小时前
RN容器启动优化实践
android·react native
恋猫de小郭12 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker17 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴17 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos