Android JetPack App Startup 使用及源码浅析(二)

简介: Android JetPack App Startup 使用及源码浅析

AppStartUp 进阶使用


手动初始化


上面我们讲解了 AppStartUp 的基本使用步骤,如果我们不像在 Application onCreate 之前执行我们的 ExampleLoggerInitializer,要怎么使用呢?


其实很简单,


第一步,在 AndroidManifest InitializationProvider 中移除 移除 <meta-data 标签

在代码中调用 AppInitializer initializeComponent 方法初始化

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


AppInitializer.getInstance(context).initializeComponent(ExampleLoggerInitializer::class.java)


App start up 源码分析


我们首先来看一下他的结构,只有简单的几个类


929e2990e264283525f077d3f4af5c17_a8689549ab9f4d1e94e53a246059f455.png


Initializer 这个接口就没有必要说了,很简单,只有两个方法。


InitializationProvider 继承了 ContentProvider,借助了 ContentProvider 会在 Application onCreate 之前执行的特点。来执行一些初始化操作。


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

我们可以看到在 onCreate 方法中调用 AppInitializer discoverAndInitialize 方法进行初始化。


  1. 找到 AndroidManifest InitializationProvider 下的 meta 便签
  2. 判断 meta 便签下 value 的值是不是 androidx.startup
  3. 判断是不是实现 Initializer 接口,是的话,执行 doInitialize 方法


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);
        // 找到 metadata 标签
        if (metadata != null) {
            Set<Class<?>> initializing = new HashSet<>();
            Set<String> keys = metadata.keySet();
            for (String key : keys) {
                String value = metadata.getString(key, null);
                // 判断 value 的值是不是 androidx.startup
                // 判断是不是实现了 Initializer 接口,是的话,反射初始化
                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();
    }
}

doInitialize 方法


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

可以看到在执行初始化的时候,先判断了是否有依赖项,有的话先执行依赖项的初始化


小结


  • App start up,我觉得他的设计初衷应该是为了收拢 ContentProvider,实际上对启动优化的帮助不是很大。
  • 如果你的项目都是同步初始化的话,并且使用到了多个ContentProvider,App Startup可能有一定的优化空间,毕竟统一到了一个ContentProvider中,同时支持了简单的顺序依赖。
  • ContentProvider 初始化的这个思想,目前有挺多 SDK 这么做的,像 FaceBook 广告 SDK,友盟 SDk 等。我们在启动优化的时候,是不是可以去掉相应的 ContentProvider,减少创建 Provider 的时间
  • 实际项目中 启动优化,大多数啊都会使用多线程异步加载,这时候 App start up 就显得很鸡肋了,没用


参考博客: Jetpack新成员,App Startup一篇就懂


本文收录于 github.com/gdutxiaoxu/… 「Android学习+面试指南」一份涵盖大部分 Android 程序员所需要掌握的核心知识。准备 Android 面试,首选 AndroidGuide!


相关文章
|
2月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
198 0
安卓项目:app注册/登录界面设计
|
2天前
|
PHP
全新uniapp小说漫画APP小说源码/会员阅读/月票功能
价值980的uniapp小说漫画APP小说源码/会员阅读/月票功能
31 20
|
1天前
|
JSON 缓存 前端开发
HarmonyOS NEXT 5.0鸿蒙开发一套影院APP(附带源码)
本项目基于HarmonyOS NEXT 5.0开发了一款影院应用程序,主要实现了电影和影院信息的展示功能。应用包括首页、电影列表、影院列表等模块。首页包含轮播图与正在热映及即将上映的电影切换显示;电影列表模块通过API获取电影数据并以网格形式展示,用户可以查看电影详情;影院列表则允许用户选择城市后查看对应影院信息,并支持城市选择弹窗。此外,项目中还集成了Axios用于网络请求,并进行了二次封装以简化接口调用流程,同时添加了请求和响应拦截器来处理通用逻辑。整体代码结构清晰,使用了组件化开发方式,便于维护和扩展。 该简介概括了提供的内容,但请注意实际开发中还需考虑UI优化、性能提升等方面的工作。
31 11
|
5天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
28 3
|
29天前
|
移动开发 小程序
仿青藤之恋社交交友软件系统源码 即时通讯 聊天 微信小程序 App H5三端通用
仿青藤之恋社交交友软件系统源码 即时通讯 聊天 微信小程序 App H5三端通用
56 3
|
1月前
|
监控 安全 开发者
山东布谷科技:关于直播源码|语音源码|一对一直播源码提交App Store的流程及重构经验
分享提交直播源码,一对一直播源码,语音源码到Appstore的重构经验!
|
1月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码服务器环境配置及app功能
一对一直播源码阿里云服务器环境配置及要求
|
3月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
150 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
1月前
|
机器人
布谷直播App系统源码开发之后台管理功能详解
直播系统开发搭建管理后台功能详解!
|
2月前
|
测试技术 数据库 Android开发
深入解析Android架构组件——Jetpack的使用与实践
本文旨在探讨谷歌推出的Android架构组件——Jetpack,在现代Android开发中的应用。Jetpack作为一系列库和工具的集合,旨在帮助开发者更轻松地编写出健壮、可维护且性能优异的应用。通过详细解析各个组件如Lifecycle、ViewModel、LiveData等,我们将了解其原理和使用场景,并结合实例展示如何在实际项目中应用这些组件,提升开发效率和应用质量。
52 6