【转】使用ContentProvider初始化Library

/ 0评 / 0

我们做项目的时候会用到很多库,不论是三方库还是二方库,又或者是自己写的,不可避免的会要去初始化它们,一方面是因为库中会需要使用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="xx.xx.xxxx.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的初始化。

注意点

使用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。

原网站:http://zjutkz.net/2017/09/11/一个小技巧——使用ContentProvider初始化你的Library/index.html

发表评论

您的电子邮箱地址不会被公开。