美文网首页Android
Android Jetpack 之 StartUp真的是太香了

Android Jetpack 之 StartUp真的是太香了

作者: android源码探索 | 来源:发表于2020-06-18 16:18 被阅读0次

我自己开发的一款填色小游戏, 运用到了很多android技术,
欢迎大家到应用市场评论下载安琪花园


Jetpack StartUp官网 需要翻墙

StartUp的作用

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

StartUp的原理

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

ContentProvider需要我们自己去写吗

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

dependencies {
    implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
}

注册ContentProvider

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

<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
里面提供的一个接口。 并重写对应的两个方法

public interface Initializer<T> {

  @NonNull
  T create(@NonNull Context context);

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

接口方法介绍

接口需要传入一个泛型, 而create方法需要返回泛型的实例。
首先看一下 create()方法的作用: 初始化的代码 就是在create方法里面执行的。

需要一个泛型, 那就创建一个类,这个类的名字是可以随便定义的,比如我创建
一个UmengInit的类,这个类对外暴露一个init方法。而这个UmengInit类,才是真正的
代码初始化的类,而暴露的这个init方法就是需要在上面的oncreate方法里执行的。
代码如下:

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改成刚创建的类的全路径即可。
比如我创建的这个类的代码如下:

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;
    }
}
image.png

结束语

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

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

dependencies()方法又有何用呢

上面的UmengInitial只是负责初始化umenng, 功能是单一的,
这个时候如果有一个图片初始化,比如叫: ImageInitial.
如何在功能上需要 ImageInitial的初始化代码要先于UmengInitial的初始化代码,那该如何实现呢?

有两种方案:

第一种方案,用到清单文章里面meta-data注册的先后顺序
第二种方案,用到dependencies()方法

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

 <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就行

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;
    }
}

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

 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();
        }
    }
 <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前沿技术
线程池原理

公众号:

qrcode_for_gh_c78cd816dc53_344.jpg

相关文章

网友评论

    本文标题:Android Jetpack 之 StartUp真的是太香了

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