Android App Startup

570 阅读2分钟

Android App Startup

  • 提供了一个 ContentProvider 来运行所有依赖项的初始化,避免每个第三方库单独使用一个 ContentProvider 进行初始化,从而提高了应用的程序的启动速度
  • 提供了简单的 API 来声明初始化组件之间的依赖关系,确保依赖项按照正确的顺序被初始化
  • 所有初始化逻辑都集中在 Initializer 类中,便于管理和维护
  • 确保每个组件只被初始化一次,即使它们被多个地方引用或依赖
implementation 'androidx.startup:startup-runtime:1.2.0'

Initializer 接口

//androidx.startup.Initializer
/**
 * Initializes library components during app startup.
 *
 * Discovered and initialized by {@link InitializationProvider}.
 *
 * @param <T> The type of the component being initialized.
 */
public interface Initializer<T> {

    /**
     * Initializes a library component within the application {@link Context}.
     *
     * @param context The application context.
     */
    @NonNull
    T create(@NonNull Context context);

    /**
     * Gets a list of this initializer's dependencies.
     *
     * Dependencies are initialized before the dependent initializer. For
     * example, if initializer A defines initializer B as a dependency, B is
     * initialized before A.
     *
     * @return A list of initializer dependencies.
     */
    @NonNull
    List<Class<? extends Initializer<?>>> dependencies();
}

InitializationProvider 类

//androidx.startup.InitializationProvider
/**
 * The {@link ContentProvider} which discovers {@link Initializer}s in an application and
 * initializes them before {@link Application#onCreate()}.
 */
public class InitializationProvider extends ContentProvider {

    @Override
    public final boolean onCreate() {
        Context context = getContext();
        if (context != null) {
            // Many Initializer's expect the `applicationContext` to be non-null. This
            // typically happens when `android:sharedUid` is used. In such cases, we postpone
            // initialization altogether, and rely on lazy init.
            // More context: b/196959015
            Context applicationContext = context.getApplicationContext();
            if (applicationContext != null) {
                // Pass the class context so the right metadata can be read.
                // This is especially important in the context of apps that want to use
                // InitializationProvider in multiple processes.
                // b/183136596#comment18
                AppInitializer.getInstance(context).discoverAndInitialize(getClass());
            } else {
                StartupLogger.w("Deferring initialization because `applicationContext` is null.");
            }
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }

    @Nullable
    @Override
    public final Cursor query(
            @NonNull Uri uri,
            @Nullable String[] projection,
            @Nullable String selection,
            @Nullable String[] selectionArgs,
            @Nullable String sortOrder) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public final String getType(@NonNull Uri uri) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public final int delete(
            @NonNull Uri uri,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public final int update(
            @NonNull Uri uri,
            @Nullable ContentValues values,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }
}

实现 Initializer 接口

  • 通过重写 dependencies 方法来实现指定组件的初始化顺序
class WorkManagerInitializer: Initializer<WorkManager> {
    override fun create(context: Context): WorkManager {
        //androidx.work.Configuration
        val configuration = Configuration.Builder().build()
        WorkManager.initialize(context, configuration)
        return WorkManager.getInstance(context)
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        // No dependencies on other libraries.
        return listOf()
    }
}
//
class ExampleLoggerInitializer:Initializer<ExampleLogger> {
    override fun create(context: Context): ExampleLogger {
        // WorkManager.getInstance() is non-null only after
        // WorkManager is initialized.
        return ExampleLogger(WorkManager.getInstance(context))
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        // Defines a dependency on WorkManagerInitializer so it can be
        // initialized after WorkManager is initialized.
        return listOf(WorkManagerInitializer::class.java)
    }
}

注册 Initializer

  • 可以在 meta-data 条目中使用 tools:node="remove" 停用单个组件的自动初始化
  • 可以在 provider 条目中使用 tools:node="remove" 停用所有组件的自动初始化
  • 这里无需为 WorkManagerInitializer 添加 meta-data 条目, 因为 WorkManagerInitializer 是 ExampleLoggerInitializer 的依赖项,可以省略
<!-- tools:node="merge" 属性合并解决冲突 -->
<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>

手动初始化组件

  • 这里会自动启动 WorkManagerInitializer,因为 WorkManagerInitializer 是 ExampleLoggerInitializer 的依赖项,可以省略
 val appInitializer = AppInitializer.getInstance(context)
 //子依赖项可以省略
 //appInitializer.initializeComponent(WorkManagerInitializer::class.java)
 appInitializer.initializeComponent(ExampleLoggerInitializer::class.java)