一个小技巧——使用ContentProvider初始化你的Library

2017-09-12 14:39:14来源:http://zjutkz.net/2017/09/11/一个小技巧——使用ContentProvider初始化你的Li作者:zjutkz's blog人点击

分享

好久好久好久没有写博客了,一来是是因为最近比较忙,二来也是因为这段时间一直在半学习半做服务端的东西,学到的东西都已经有了很不错的文章,不想重复造轮子



这两天项目里用到了Google新开源的Lifecycle组件
,用之前粗粗的看了一下源码了解了它的工作原理,感觉确实是一个有用的库就毫不犹豫的引了进来,今天在测试的时候发现了一个bug,于是详细的看了其中的源码,把bug解决掉之后,觉得Lifecycle有一个值得我们学习的优点——就是我题目中说的,使用ContentProvider初始化某一个Library,碰巧今晚有一点多余的时间,就决定写博客记录一下。


现状

我们做项目的时候会用到很多库,不论是三方库还是二方库,又或者是自己写的,不可避免的会要去初始化它们,一方面是因为库中会需要使用Context进行一些操作,比如startActivity,又或者getResource;另一方面在初始化的时候我们可以传递一些缺省值给对应的库。如果你的库在初始化的时候只需要传递一个Context(例如前面提到的Lifecycle又或者是Firebase),那么我们现在会在Application里这么写:


@Override
public void onCreate() {
super.onCreate();
MyLib.init(this);
}

通过init方法传递一个Application进去并持有它。


这样做没什么错,但是在我看来是不太好的。一方面如果使用者忘记初始化它了,很可能会出现NPE(我之前就犯过这种错误,简直想撞墙);另一方面,当你需要初始化十几二十个库的时候,代码就会变得比较庞大了。


其他方式

但是如果你使用过Lifecycle或者Firebase,你会发现你根本不用写哪怕一行的初始化代码就可以使用它,那么它们是怎么做的呢?


以Lifecycle为例:


我们去看Lifecycle的Manifest,其中会注册一个ContentProvider:


<provider
android:name="android.arch.lifecycle.LifecycleRuntimeTrojanProvider"
android:authorities="com.vdian.tuwen.lifecycle-trojan"
android:exported="false"
android:multiprocess="true" />

而这个Provider的实现非常的简单:


public class LifecycleRuntimeTrojanProvider extends ContentProvider {
@Override
public boolean onCreate() {
LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, String[] strings, String s, String[] strings1,
String s1) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
return null;
}
@Override
public int delete(@NonNull Uri uri, String s, String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
}

可以看到除了onCreate,其他的方法都没有逻辑,而onCreate里的逻辑也十分简单,就是去进行Lifecycle的初始化:


@Override
public boolean onCreate() {
LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
return true;
}

我们都知道ContentProvider的onCreate的调用时机介于Application的attachBaseContext和onCreate之间,Lifecycle这么做,把init的逻辑放到库内部,让调用方完全不需要在Application里去进行初始化了,十分方便。


注意点

使用ContentProvider初始化的方式,我们需要注意一下几点:


首先,在Manifest里设置ContentProvider的时候要设置一个authorities,这个authorities相当于ContentProvider的标识,是不能重复的,为了保证不重复,我们再设置这个值的时候最好不要硬编码,而是使用以下的这种方式:


<provider
android:authorities="${applicationId}.cp"
android:name=".MyLibRuntimeProvider"
android:exported="false"/>

使用gradle的placeholder,可以比较有效的避免重复,Lifecycle就是这么做的。


其次,如果你的App的多进程架构的,可以将multiprocess设置成true,以保证每一个进程都会初始化该ContentProvider:


<provider
android:authorities="${applicationId}.cp"
android:name=".MyLibRuntimeProvider"
android:exported="false"
android:multiprocess="true"/>

最后,如果你的App使用到了Application替换技术,可能是hotpatch,也可能是dex加壳等等,需要注意你的ProxyApplication的代码,因为这个时候在ContentProvider中getContext()获得的是你的ProxyApplication,而不是App真正的Application。


最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台