【绝非标题党】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();
            }
        }
    }
相关推荐
JhonKI29 分钟前
【MySQL】存储引擎 - CSV详解
android·数据库·mysql
开开心心_Every39 分钟前
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
android·windows·python·搜索引擎·智能手机·pdf·音视频
大G哥1 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
鸿蒙布道师5 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork5 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
xiangxiongfly9157 小时前
Android setContentView()源码分析
android·setcontentview
人间有清欢8 小时前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
人间有清欢9 小时前
Android开发报错解决
android
每次的天空10 小时前
Android学习总结之kotlin协程面试篇
android·学习·kotlin
每次的天空12 小时前
Android学习总结之Binder篇
android·学习·binder