我自己开发的一款填色小游戏, 运用到了很多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前沿技术
线程池原理
公众号:
网友评论