【绝非标题党】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();
            }
        }
    }
相关推荐
_一条咸鱼_几秒前
大厂Android面试秘籍:Activity 布局加载与视图管理(五)
android·面试·kotlin
re1ife5 小时前
Android Studio开发知识:从基础到进阶
android·java·开发语言·android studio
吴同学是个程序员5 小时前
【Android】Android Studio 配置国内镜像源
android·ide·gradle·android studio·hosts
jiet_h9 小时前
使用 Ktor 构建现代 Android 应用的后端服务
android
深漂阿碉12 小时前
Android studio2024的第一个安卓项目
android
zilong_zzz12 小时前
文件IO4(提高LCD显示效率/BMP图像原理与应用)
android
_一条咸鱼_13 小时前
大厂Android面试秘籍:Activity 窗口管理模块(四)
android·面试·android jetpack
无极程序员16 小时前
远程主机可能不符合glibc和libstdc++ VS Code服务器的先决条件
android·java·运维·服务器·php
快乐10117 小时前
Mac下FFmpeg编译和集成
android