Android 自动初始化

2,835 阅读3分钟
原文链接: tanfujun.com

原文地址: https://firebase.googleblog.com/2016/12/how-does-firebase-initialize-on-android.html Firebase 介绍了他们在 Android 初始化的一些做法

如果你在使用 Firebase 你会发现你可能不用写任何初始化的代码。你可以直接使用你需要的对象。使用 crash reporting 的时候你甚至不用写任何代码就能让它开始工作,在2016的 Google I/O 我谈了一点点,这篇文章里我会详细的说一说。

问题

许多的 SDK 需要一个 Android Context 来让他们工作。这个Context 是连接到 Android 运行时,让 SDK 可以使用系统的资源 服务 广播等等。许多 SDK 需要你执行初始化,传递一个 Context,这样只要你的应用进程是活着的他们就可以使用系统资源,一般是在 Application 的自定义子类进行初始化,如下的代码很常见。


public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        SomeSdk.init(this);  // init some SDK, MyApplication is the Context
    }
}

是的,你得在你的 manifest 里面指定application name:


<application
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:name="package.of.MyApplication"
    ... >

但是 FireBase 有一些不同的做法

解决方案

FireBase 使用了一个小技巧,利用 ContentProvider 来初始化,不用开发者编写任何代码。使用 ContentProvider 的原因有2个:

  1. 他们创建初始化在优先于其他组件(主线程),在 Activity、Service、BroadcastReceivers 之前

  2. 在 manifest 合并的时候(编译期间) , 会把 Android 库里面的 ContentProvider 和 主 manifest 合并到一起。

我们来看看这2点。

ContentProvider 初始化更早

当一个 App 进程启动的时候有明确的操作顺序:

  1. manifest 中的 ContentProvider 被按照优先顺序创建

  2. Application 类或者他的子类被创建

  3. 其他被 Intent 调用的组件创建。

当ContentProvider创建的时候,在 onCreate 方法中可以 getContext() 拿到Context。可以无限期的持有。

这里你也可以设定 ActivityLifeCallBacks(FireBase 用于 Firebase Analytics)或者 设置 UncaughtExceptionHandler (FireBase 用于 Firebase Crash Reporting). 你在这里初始化一个依赖注入框架也是可以的。

ContentProvider 的声明会被合并

在构建过程中 manifest merger 会合并所有库的 manifest。

所以在你的库项目声明一个 ContentProvider 让他的 onCreate 会自动执行避免了初始化代码。

FirebaseInitProvider 初始化你的app(惊喜)

所有使用 Firebase 的应用在某种程度上都会依赖于 firebase-common 库。这个库暴露了 FirebaseInitProvider ,这个类是为了执行 FirebaseApp.initializeApp 通过配置的 Google Service json 文件初始化默认 FirebaseApp 实例,这些配置通过 Google Servie 插件作为 Android 资源参与到构建中。然而,如果你在一个应用程序中引用多个Firebase项目,你就必须编写代码来初始化其他firebaseapp实例,在 之前的博客里有讨论。

使用 ContentProvider 初始化的弊端

如果你用 ContentProvider 来初始化你的 app 或者库,你要记得几件事。

第一,一个 “authority” 在一台 Android 设备上只能对应一个 ContentProvider。如果你的库给同一个设备上的不同应用使用,你要保证 “authority” 字符串是不同的,否则第二个应用会被拒绝安装。这个是在 xml 中定义的,通常是一种硬编码,但是有一个小技巧来保证他们不相同。

gradle 构建中可以使用manifest placeholders 使用这个特性利用 application id 来保证不同:


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

另一件事是 ContentProvider 是运行在主进程的,对绝大多数应用来说没什么问题,因为他们是单进程应用。但是当你是多进程应用的时候,另一个进程不会让 ContentProvider onCreate 调用。在这种情况下,你的 app 不能调用和初始化的东西相关的类,或者用另一种方式初始化。请注意和 Application 或者 Application 子类不一样,这种方式不是每个进程都会调用。

这样使用 ContentProvider 值得吗?

是的,看起来有点怪,这个 ContentProvider 不提供任何内容,他的实现方法你应该返回 null。但是事实证明,这样初始化非常可靠,自动完成并且不需要任何代码。很棒!