一般需要初始化的sdk都会对外提供一个初始化方法供外界调用,如: 对调用者很不友好。另一种做法是使用 然后在 这样初始化的逻辑就由Sdk开发者在内部完成了。 但是,如果一个app依赖了很多需要初始化的sdk,如果都放在一个 在App模块的 app通过 例如有一个 泛型 但是如果这个sdk依赖于其它的sdk,必须在其它sdk初始化之后才能初始化,则需要在 在 我们需要告诉 通常每一个初始化器对应一个 这里的 如果有一个 为了减少app启动时间,对于一些非必须的初始化应该在app启动后、sdk使用前完成初始化,使用在 同时需要失能 通过 只有五个类文件,除去 可以看到,在 用来做懒加载的 优点: 缺点:
Android架构组件–App Startup
1.解决的问题
public class App extends Application { @Override public void onCreate() { super.onCreate(); Sdk1.init(this); } }
ContentProvider
初始化,如下:public class Sdk1InitializeProvider extends ContentProvider { @Override public boolean onCreate() { Sdk1.init(getContext()); return true; } ... }
AndroidManifest.xml
文件中注册这个privoder
,如下:<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="cn.zhengzhengxiaogege.sdk1"> <application> <provider android:authorities="${applicationId}.init-provider" android:name=".Sdk1InitializeProvider" android:exported="false"/> </application> </manifest>
ContentProvider
中会导致此ContentProvider
代码数量增加。而且每增加一个需要初始化的sdk都要对该ContentProvider
文件做改动,不方便合作开发。而如果每个sdk都采用同样的方式将会带来性能问题。App Startup library
可以有效解决这个问题。2.使用
App StartUp
(1)添加依赖
build.gradle
文件中添加依赖:dependencies { implementation "androidx.startup:startup-runtime:1.0.0-alpha01" }
(2)实现
Initializer<T>
接口Initializer<T>
接口接入App Startup
,需要实现两个方法public interface Initializer<T> { @NonNull T create(@NonNull Context context); @NonNull List<Class<? extends Initializer<?>>> dependencies(); }
Sdk1
如下:public class Sdk1 { private static final String TAG = "Sdk1"; private static Context sApplicationContext; private static volatile Sdk1 sInstance; public static void init(Context applicationContext){ sApplicationContext = applicationContext; Log.e(TAG, "Sdk1 is initialized"); } public static Sdk1 getInstance(){ if (sInstance == null) { synchronized (Sdk1.class){ if (sInstance == null) { sInstance = new Sdk1(); } } } return sInstance; } private Sdk1(){ } public void printApplicationName(){ Log.e(TAG, sApplicationContext.getPackageName()); } }
sdk1
对外提供Sdk1
类,包含初始化方法init(Context)
,实例获取方法getInstance()
和对外的服务方法printApplicationName()
。为了使用App Startup
,需要提供一个初始化器如下:public class Sdk1Initializer implements Initializer<Sdk1> { @NonNull @Override public Sdk1 create(@NonNull Context context) { Sdk1.init(context); return Sdk1.getInstance(); } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return Collections.emptyList(); } }
T
为待初始化的Sdk对外提供的对象类型;create(Context)
方法是该Sdk
初始化
逻辑写入的地方,其参数context
为Application Context
,同时需要返回一个Sdk对外提供的对象实例。dependencies()
方法则需要返回一个列表,这个列表需要给出一个该Sdk依赖的其它
Sdk的初始化器,也就是这个列表决定了哪些sdk会在这个sdk之前初始化,如果这个sdk是独立的没有依赖与其它的sdk,可以将该方法返回一个空列表(如Sdk1Initializer
的实现)。dependencies()
方法中指明。例如现在有一个sdk2
也需要初始化,且它必须在sdk1
初始化之后才能初始化,那么sdk2
的初始化器的实现如下:public class Sdk2Initializer implements Initializer<Sdk2> { @NonNull @Override public Sdk2 create(@NonNull Context context) { Sdk2.init(context); return Sdk2.getInstance(); } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { List<Class<? extends Initializer<?>>> dependencies = new ArrayList<>(); dependencies.add(Sdk1Initializer.class); return dependencies; } }
dependencies()
方法中指明了Skd2
的依赖项,因此App Startup
会在初始化sdk2
之前先初始化sdk1
。(3)注册
Provider
和Initializer<?>
App Startup
我们实现了哪些Sdk初始化器(Sdk1Initializer
、Sdk2Initializer
)。同时App Startup
并没有提供AndroidManifest.xml
文件,因此App Startup
用到的provider
同样需要注册。在app的AndroidManifest.xml
文件添加如下代码:<provider android:authorities="${applicationId}.androidx-startup" android:name="androidx.startup.InitializationProvider" android:exported="false" tools:node="merge"> <meta-data android:name="cn.zhengzhengxiaogege.appstartupstudy.Sdk1Initializer" android:value="@string/androidx_startup"/> <meta-data android:name="cn.zhengzhengxiaogege.appstartupstudy.Sdk2Initializer" android:value="@string/androidx_startup"/> </provider>
<meta-data>
标签,但是如果有些初始化器已经被一个已
经注册的初始化器依赖(比如Sdk1Initializer
已经被Sdk2Initializer
依赖),那么
可以不用在AndroidManifest.xml
文件中显式地指明,因为App Startup
已经通过
注册的Sdk2Initializer
找到它了。<meta-data>
标签的value
属性必须指定为字符串androidx_startup
的值,
也就是("androidx.startup"
),否则将不生效。sdk3
内部通过App Startup
帮助使用者处理了初始化,那么sdk3
的AndroidManifest.xml
文件中已经存在了InitializationProvider
的provider
标签,此时会与app模块中的冲突,因此在app模块的provider
标签中指明tools:node="merge"
,通过AndroidManifest.xml
文件的合并机制。3.
App StartUp
实现懒加载provider
中注册的方法不能达到这个目的。App StartUp
提供了AppInitializer
来解决这个问题。如下,在需要初始化的位置使用AppInitializer
:public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "MainActivity Created"); AppInitializer.getInstance(getApplicationContext()) .initializeComponent(Sdk2Initializer.class); Sdk1.getInstance().printApplicationName(); } }
AndroidManifest.xml
文件中的对应初始化器的<meta-data>
,如下:<provider android:authorities="${applicationId}.androidx-startup" android:name="androidx.startup.InitializationProvider" android:exported="false" tools:node="merge"> <meta-data android:name="cn.zhengzhengxiaogege.appstartupstudy.Sdk2Initializer" android:value="@string/androidx_startup" tools:node="remove"/> </provider>
tools:node="remove"
来标记该初始化器。这样会在AndroidManifest.xml
文件合并时将这个<meta-data>
移除掉,否则该初始化器仍会在Application
中被初始化并标记为已经初始化,后面的懒加载将不执行任何初始化操作,相当于使懒加载失效了。4.剖析
App StartUp
App StartUp
的设计思路比较简单,就是将多个需要初始化的Sdk在一个provider
中完成,从而减少多个provider
带来的性能问题和繁杂的AndroidManifest.xml
文件声明。目前App StartUp
为1.0.0-alpha01
版本,其代码结构非常简单。StartupException
和StartupLogger
,其核心类只有三个。Intializer.java
的作用很简单,就是为lib的使用者提供了接入的方法,因此不再赘述。InitializationProvider.java
是App StartUp
中使用的单一的provider
,所有注册的初始化将在这个provider
中完成。App StartUp
只重写了OnCreate()
方法:public final class InitializationProvider extends ContentProvider { @Override public boolean onCreate() { Context context = getContext(); if (context != null) { AppInitializer.getInstance(context).discoverAndInitialize(); } else { throw new StartupException("Context cannot be null"); } return true; } ...
OnCreate()
方法中执行了扫描和初始化注册的组件。其实现方法在
AppInitializer.java
中。AppInitializer
内部是一个单例实现,它的getInstance(Context)
方法传入的是Application
级别的Context
,并将其传递给注册的各Intializer
,首先看discoverAndInitialize()
方法:@SuppressWarnings("unchecked") void discoverAndInitialize() { try { Trace.beginSection(SECTION_NAME); // 扫描并获取Manifest文件中的 `InitializationProvider`这个组件中注册的<meta-data> // 信息 ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName()); ProviderInfo providerInfo = mContext.getPackageManager() .getProviderInfo(provider, GET_META_DATA); Bundle metadata = providerInfo.metaData; // 然后遍历<meta-data>标签,获取到每个标签的 `Initializer`并对其初始化 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) { // 注意这里会用<meta-data>标签的value属性的值和`@string/androidx_startup` // 对比,只有是这个值的<meta-data>标签才会被初始化。 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(); } }
discoverAndInitialize()
方法首先扫描清单文件获取到需要初始化的初始化器Initializer
,然后执行初始化操作,即调用doInitialize(Class<? extends Initializer<?>>, Set<Class<?>>)
方法,如下:@NonNull @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) <T> T doInitialize( @NonNull Class<? extends Initializer<?>> component, @NonNull Set<Class<?>> initializing) { synchronized (sLock) { boolean isTracingEnabled = Trace.isEnabled(); try { if (isTracingEnabled) { Trace.beginSection(component.getSimpleName()); } // `initializing`存储着正在初始化的初始化器。 // 这个判断是要解决循环依赖的问题,比如 `Sdk1Initializer`依赖了它本身,或者是 // `Sdk1Initializer` 依赖了 `Sdk2Initializer`,同时 `Sdk2Initializer` 又 // 依赖了 `Sdk1Initializer`,这是存在逻辑错误的,因此需要排除。 if (initializing.contains(component)) { String message = String.format( "Cannot initialize %s. Cycle detected.", component.getName() ); throw new IllegalStateException(message); } // `mInitialized` 是一个 `Map<Class<?>, Object>`,它缓存了已经执行过初始化的 // `Initializer`的 `Class` 对象和初始化的结果,通过这种方法来避免重复初始化。 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())); } // 调用初始化器的 `create(Context)`方法,执行具体的初始化逻辑。 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(); } } }
doInitialize(Class<? extends Initializer<?>>, Set<Class<?>>)
方法首先会实例化一个初始化器,然后通过dependencies()
方法找到它依赖的初始化器做递归初始化,这个过程中如果遇到诸如依赖自身、循环依赖等逻辑错误问题将抛出异常。处理完依赖后调用它的create(Context)
方法执行具体的初始化逻辑。最后初始化完成,将状态和结果缓存,防止多次初始化。initializeComponent(Class<? extends Initializer<T>>)
的方法就比较简单了,它直接调用doInitialize(Class<? extends Initializer<?>>, Set<Class<?>>)
方法对指定的初始化器做初始化,如下:@NonNull @SuppressWarnings("unused") public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) { return doInitialize(component, new HashSet<Class<?>>()); }
5.
App StartUp
利弊
Application
文件和Mainfest
文件需要频繁改动的问题,同时也减少了Application
文件和Mainfest
文件的代码量,更方便维护了ContentProvider
,减少性能损耗。ContentProvider
做初始化的能力,并精简了sdk的使用流程。
Initializer<>
的实现类,在低版本系统中会有一定的性能损耗。Initializer<>
的实现类提供一个无参构造器,当然也不能算是缺点,如果缺少的话新版的android studio会通过lint检查给出提醒。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算