Android Jetpack 架构组件(八) App startup

博客介绍了AppStartup如何解决Application的onCreate方法过于臃肿和ContentProvider初始化耗时的问题。通过在ContentProvider中集中初始化,AppStartup实现了模块化的异步初始化,并允许控制初始化顺序。同时,它允许延迟初始化某些模块,减少了不必要的资源消耗。内容包括AppStartup的简单使用、依赖管理以及源码解析。
摘要由CSDN通过智能技术生成

讲App startup前,先看看下面的代码,在Application里的onCreate()里面好多的init()方法。

public class MyApplication extends Application {

    private static MyApplication instance;

    public static MyApplication getInstance() {
        return instance;
    }


    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;

        initLog4J();
        UMConfigure.init(this, UMConfigure.DEVICE_TYPE_PHONE, "xxxxxxxxxxxxxxxx");
        ToastUtil.getInstance().init(this);
        NetworkUtil.getInstance().init(this);
        xxxxManager.getInstance().init(this);
        P2PManager.getInstance().init(this);
        DLNAManager.getInstance().init(this);
        PaymentManager.getInstance().init(this);
        ExternalADManager.getInstance().init(this);
        GAManager.getInstance().init(this);
        mUmengChannel = AnalyticsConfig.getChannel(this);
        ErrorReporter.getInstance().init(this);
        SharedInfoService.getInstance(this);
        AbTestRemoteConfig.getInstance().setup();

    }
}

之前可以用ContentProvider,把各个模块的初始化都分散出去。但是又导致了问题,ContentProvider毕竟是四大组件之一,听过一个空ContentProvider的初始化就耗费了2ms。那十个ContentProvider就20ms了。

而App startup的出现,既可以解决Application 的onCreate方法臃肿问题,又可以解决ContentProvider耗费性能的问题。

原理也很简单,就是在一个ContentProvider里面,把所有初始化都给办了。

但是你说我上面那种把初始都写在一起,和之后改用 app startup模式后,相比性能改善了,那也是不对的!

 

ContentProvider本身是拿来数据共享的。但是由于在app启动过程中,ContentProvider也会被系统自动调用onCreate()方法。所以大家都拿它来做启动初始化了。下面是ContentProvider的启动过程。都还在application启动之前。

 

简单使用:

这是广告模块的初始化。本来是要放到application onCreate()方法里面初始化的。

public class AdvInitializer implements Initializer<Object> {
    @NonNull
    @Override
    public Object create(@NonNull Context context) {
        AdvManager.getInstance().init(context);
        return null;
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        return Collections.emptyList();
    }
}

在AndroidMenifest.xml中添加:

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">

            <meta-data  android:name="com.my.adv.AdvInitializer"
                android:value="androidx.startup" />
        </provider>

以上这两部做完就可以了。app启动的时候就会自动调用AdvInitializer 的create方法。

注意点:

1. dependencies() 方法的作用:解决多个模块初始化先后顺序的问题。

    如果你的初始化还依赖别的模块先初始化,那么dependencies()就可以解决这个问题。返回你依赖模块的Initializer list就可以了。系统会帮你按顺序调用Initializer 。最后再调用你模块的Initializer。

比如:

    @Override
    public List<Class<Initializer<?>>> dependencies() {
        // 定义模块依赖LogManager,需要先初始化LogManager。LogInitializer会先被调用,从而初始化 
        // LogManager,然后在初始化你的模块。
        return Arrays.asList(LogInitializer.class);
    }

2. 如果不想这样自动初始化所有了。那么只要把<provider>的tools:node="merge" 改成tools:node="remove"就好了。

3. 同样,不想让某个模块自动初始化, 那么也只要,在相应的<meta-data里>添加tools:node="remove"就好了。

4.当你把某个模块的初始化改成手动后。你可以直接调用下面的代码:

AppInitializer.getInstance(context)
    .initializeComponent(AdvInitializer.class);

5. App startup的初始化都是在UI线程同步的。所以想要异步线程同步的同时,又支持先后顺序,那是不行的。

6. 有利于解决模块化依赖问题。当然本来就可以用ContentProvider解决。只是用App startup解决了多ContentProvider的耗时问题。

源码解析:

InitializationProvider .java

这个类就是AndroidManifest里定义的provider实现类。有用的代码就一句: AppInitializer.getInstance(context).discoverAndInitialize();

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;
    }

    @Nullable
    @Override
    public 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 String getType(@NonNull Uri uri) {
        throw new IllegalStateException("Not allowed.");
    }

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

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

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

AppInitializer.java

再看这个类,重要的就这两个方法。

@SuppressWarnings("WeakerAccess")
public final class AppInitializer {

    @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) {
                    // Use the simpleName here because section names would get too big otherwise.
                    Trace.beginSection(component.getSimpleName());
                }
                if (initializing.contains(component)) {
                    String message = String.format(
                            "Cannot initialize %s. Cycle detected.", component.getName()
                    );
                    throw new IllegalStateException(message);
                }
                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()));
                        }
                        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();
            }
        }
    }

    @SuppressWarnings("unchecked")
    void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);
            ComponentName provider = new ComponentName(mContext.getPackageName(),
                    InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager()
                    .getProviderInfo(provider, GET_META_DATA);
            Bundle metadata = providerInfo.metaData;
            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) {
                    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;
                            mDiscovered.add(component);
                            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方法中,解析获取Androidmanifest中定义的所有meta-data, 把所有的initializer 都放到mDiscovered里面存起来。调用doInitialize()完成初始化。
  • doInitialize方法中,完成各个模块的初始化。并且用递归调用doInitialize解决链条依赖问题。里面还是用了反射来初始化各个模块的initializer

再总结一下:

1.底层是contentprovider

2.contentprovider初始化生命周期在 attachebaseContext,然后是contentprovider oncreate ,然后才是Application onCreate

3.用contentprovider可以省去三方库的 context问题

4.但是三方库都用contentprovider 这个办法,本身也耗时。

5.所以用一个contentprovider 串行三方库的初始化问题。

6.如果不想在contentprovider的生命周期就初始化想延迟初始化怎么办?remove

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值