Android JetPack之启动优化StartUp初始化组件的详解和使用

news2024/9/30 7:29:57

一、背景

 先看一下Android系统架构图

        在Android设备中,设备先通电(PowerManager),然后加载内核层,内核走完,开始检查硬件,以及为硬件提供的公开接口,然后进入到库的加载。库挂载后开始Init进程初始化,调用init.rc。紧接着就是Android系统的核心模块Zygote进程的初始化,这些开始走到了Framework层,Zygote会fork各种进程,AMS,WMS,Handler等,并且AMS和WMS以及其他的service都会被SystemService存储起来,通过SystemServiceManager来管理,同样MainThread主进程也会在Zygote进程中创建。在创建各种service的时候,同样也会这些服务提供了binder,这样就可以和这些service进行通讯。

        为什么要通过这张图来分析Andtroid系统?那是因为我们熟悉了整个启动流程才会更好的使用这个框架,简单、高效的完成组件加载。

二、常见的Application中加载机制

        在正常开发过程中,模块为了长期存活在应用中,都会提前抢占application的入口。在application中,比较常见的都是直接在oncreate方法中执行。因为APP启动,是有AMS或者ATMS来完成application的创建(这就是为什么在上面介绍Android系统架构)。第一次启动正确的耗时应该从attachBaseContext()到第一个第一个Activity的onResume()的完成。这样加载机制,运行在主线程中,又会引起另外一个问题,那就是卡顿和启动时间过长。

        针对这个问题,google提供了一个startup库,实现一种在应用启动时初始化组件的简单而高效的方法。

三、StartUp组件

startup库,实现一种在应用启动时初始化组件的简单而高效的方法。借助 App Startup 库,可在应用启动时简单、高效地初始化组件。库开发者和应用开发者都可以使用 App Startup 来简化启动序列并显式设置初始化顺序。

 依赖库

implementation 'androidx.startup:startup-runtime:1.1.1'

代码库:

        核心的如上面包里的类所示。任何需要初始化的节点,都需要实现Initializer的接口。

public interface Initializer<T> {

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

    /**
     * @return A list of dependencies that this {@link Initializer} depends on. This is
     * used to determine initialization order of {@link Initializer}s.
     * <br/>
     * For e.g. if a {@link Initializer} `B` defines another
     * {@link Initializer} `A` as its dependency, then `A` gets initialized before `B`.
     */
    @NonNull
    List<Class<? extends Initializer<?>>> dependencies();
}

  1. oncreate:初始化组件所需的所有操作,并返回T的实例。
  2. dependencies:依赖项,如果当前的组件在初始化的时候,依赖其他组件,将其他初始化项添加进来即可。(即需赖的Initializer初始化后,才会创建自己)

案例:

class MyInitializer(): Initializer<Boolean> {

    override fun create(context: Context): Boolean {


        Log.e("init","create-A")
       

        return true
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {

        Log.e("init","dependencies-A")
        return mutableListOf()
    }
}

启动StartUp

启动startUp,分为两种情况,一种是自动,还有一种是手动。

  1. 自动:

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data
                android:name="com.example.wiik.testdemo.startup.MyInitializer"
                android:value="androidx.startup" />

        </provider>

provider:添加startUp的provider。

<meta-data>:就是我们初始化的对象类

执行如下:

 自动完成其实是调用了

AppInitializer.getInstance(context).discoverAndInitialize();

    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;
            discoverAndInitialize(metadata);
        } catch (PackageManager.NameNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }

  1. 手动调用:

手动调用也是需要注册provider,新增meta-data元素,但是手动调用需要新增一个节点,

tools:node="remove"

申请1:在meta-data节点下申请,那只有这一个meta-data的init对象是手动,其他还是自动完成

申请2:在provider节点下申请,那provider节点下所有的节点都是需要时手动创建

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data
                android:name="com.example.wiik.testdemo.startup.MyInitializer"
                android:value="androidx.startup" />
            <meta-data
                android:name="com.example.wiik.testdemo.startup.HandInitializer"
                android:value="androidx.startup"
                tools:node="remove" />


        </provider>

手动调用:

boolean flag = AppInitializer.getInstance(this).initializeComponent(HandInitializer.class);
Log.e("init","hand return value="+flag);

日志如下:

1、 在启动过程中,如果有自动和手动,那么先启动自动,再启动手动。

2、如果你的应用在init阶段有返回值,那么自动初始化无法完成,只有手动才能接收。


3、依赖dependencies

依赖是一种前置条件,这种前置一般需要提前初始化,如下。

先定义一个前置的Init对象

class ProposeInitializer(): Initializer<Boolean> {

    override fun create(context: Context): Boolean {


        Log.e("init","create-A-ProposeInitializer")
        WorkManager.initialize(context.applicationContext,Configuration.Builder().build());

        return true
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {

        Log.e("init","dependencies-C-ProposeInitializer")
        return mutableListOf()
    }
}

引用前置Initer的类

class MyInitializer(): Initializer<Boolean> {

    override fun create(context: Context): Boolean {


        Log.e("init","create-A-AUTO")

        return true
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return mutableListOf(ProposeInitializer::class.java)
    }
}

分析:

执行顺序,A依赖了C,先执行A的 依赖,在执行C的依赖,在执行C的创建,在执行A的创建

这个时候即使C是手动模式,也会变成自动。

如果C还依赖其他,将会遍历其他的。

是否是自动始化:isEagerlyInitialized

判断一个对象是否是自动,

  public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) {
        // If discoverAndInitialize() was never called, then nothing was eagerly initialized.
        return mDiscovered.contains(component);
    }

这个类的方法在provider会被提前执行。

四、总结

        这样,我们就完成了startUp的基本架构分析与使用,如果你需要使用该框架,可以将自己的类管里起来。   

 注意:

        如果发现启动失败,看下前置条件,是否满足     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373395.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Winform控件开发(16)——Timer(史上最全)

前言: Timer控件的作用是按用户定义的时间间隔引发事件的计时器,说的直白点就是,他就像一个定时炸弹一样到了一定时间就爆炸一次,区别在于定时炸弹炸完了就不会再次爆炸了,但是Timer这个计时器到了下一个固定时间还会触发一次,上面那张图片就是一个典型的计时器,该定时器…

【Java】Spring Boot 配置文件

文章目录SpringBoot 配置文件1. 配置文件的作用2. 配置文件的格式3. properties配置文件说明3.1 properties基本语法3.2 读取配置文件3.3 properties缺点分析4. yml配置文件说明4.1 yml基本语法4.2 yml使用进阶4.2.1 yml配置不同的数据类型及null4.2.1 yml配置的读取4.2.2 配置…

Python蓝桥杯训练:基本数据结构 [哈希表]

Python蓝桥杯训练&#xff1a;基本数据结构 [哈希表] 文章目录Python蓝桥杯训练&#xff1a;基本数据结构 [哈希表]一、哈希表理论基础知识1、开放寻址法2、链式法二、有关哈希表的一些常见操作三、力扣上面一些有关哈希表的题目练习1、[有效的字母异位词](https://leetcode.cn…

0101基础概念-图-数据结构和算法(Java)

文章目录1 图1.1 定义1.2 4种图模型2 无向图2.1 定义2.2 术语后记1 图 1.1 定义 图是一种非线性的数据结构&#xff0c;表示多对多的关系。 图&#xff08;Graph&#xff09;是由顶点的有穷非空集合和顶点之间边的集合组成&#xff0c;通常表示为&#xff1a;G(V, E)&#xf…

ecology9-谷歌浏览器下-pdf.js在渲染时部分发票丢失文字 问题定位及解决

问题 问题描述 &#xff1a; 在谷歌浏览器下&#xff0c;pdf.js在渲染时部分发票丢失文字&#xff1b;360浏览器兼容模式不存在此问题 排查思路&#xff1a;1、对比谷歌浏览器的css样式和360浏览器兼容模式下的样式&#xff0c;没有发现关键差别 2、✔使用Fiddler修改网页js D…

什么是线程死锁?如何解决死锁问题

死锁&#xff0c;一组互相竞争的资源的线程之间相互等待&#xff0c;导致永久阻塞的现象。 如下图所示&#xff1a; 与死锁对应的&#xff0c;还有活锁&#xff0c;是指线程没有出现阻塞&#xff0c;但是无限循环。 有一个经典的银行转账例子如下&#xff1a; 我们有个账户类…

操作系统权限提升(十四)之绕过UAC提权-基于白名单AutoElevate绕过UAC提权

系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权 注&#xff1a;阅读本编文章前&#xff0c;请先阅读系列文章&#xff0c;以免造成看不懂的情况&#xff01;&#xff01; 基于白名单AutoElevate绕过…

2-MATLAB APP Design-下拉菜单栏的使用

一、APP 界面设计展示 1.新建一个空白的APP,在此次的学习中,我们会用到编辑字段(文本框)、下拉菜单栏、坐标区,首先在界面中拖入一个编辑字段(文本框),在文本框中输入内容:下拉菜单栏的使用,调整背景颜色,字体的颜色为黑色,字体的大小调为26. 2.在左侧组件库常用栏…

Qt音视频开发17-vlc内核回调拿图片进行绘制

一、前言 在众多播放器中&#xff0c;支持的种类格式众多&#xff0c;并支持DVD影音光盘&#xff0c;VCD影音光盘及各类流式协议&#xff0c;提供了sdk进行开发&#xff0c;这点是至关重要的&#xff0c;尽管很多优秀的播放器很牛逼&#xff0c;由于没有提供sdk第三方开发&…

【网络编程套接字(一)】

网络编程套接字&#xff08;一&#xff09;理解源IP地址和目的IP地址理解源MAC地址和目的MAC地址理解源端口号和目的端口号PORT VS PID认识TCP协议和UDP协议网络字节序socket编程接口socket常见APIsockaddr结构简单的UDP网络程序服务端创建套接字服务端绑定字符串IP VS 整数IP客…

面试官: 你知道 JWT、JWE、JWS 、JWK嘛?

想起了 之前做过的 很多 登录授权 的项目 它相比原先的session、cookie来说&#xff0c;更快更安全&#xff0c;跨域也不再是问题&#xff0c;更关键的是更加优雅 &#xff0c;所以今天总结了一篇文章来介绍他 JWT 指JSON Web Token&#xff0c;如果在项目中通过 jjwt 来支持 J…

Springboot整合 Thymeleaf增删改查一篇就够了

很早之前写过Thymeleaf的文章&#xff0c;所以重新温习一下&#xff0c;非前后端分离&#xff0c;仅仅只是学习 官网&#xff1a; https://www.thymeleaf.org/ SpringBoot可以快速生成Spring应用&#xff0c;简化配置&#xff0c;自动装配&#xff0c;开箱即用。 JavaConfigur…

【java基础】枚举类(enum)

文章目录基本介绍快速使用字段、方法、构造器枚举类方法toString方法valueOf方法values方法ordinal方法基本介绍 在java中有一种特殊的类型就是枚举类&#xff0c;对于一个有限的有固定值的集合&#xff0c;我们就可以考虑使用枚举类来进行表示&#xff0c;例如服装的大小为 小…

linux shell 入门学习笔记15 shell 条件测试

概念 shell的条件测试目的是得出真和假。 shell 提供的条件测试语法 test 命令 [] 中括号命令 语法*&#xff1a; test条件测试 test命令用来评估一个表达式&#xff0c;他的结果是真&#xff0c;还是假&#xff0c;如果条件为真&#xff0c;那么命令执行状态结果就为0&…

【蓝桥杯集训·周赛】AcWing 第92场周赛

文章目录第一题 AcWing 4864. 多边形一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解第二题 AcWing 4865. 有效类型一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解第三题 AcWing 4866. 最大数量一、题目1、原…

Spring之丐版IOC实现

文章目录IOC控制反转依赖注入Bean的自动装配方式丐版IOC实现BeanDefinition.javaResourceLoader.javaBeanRegister.javaBean和DI的注解BeanFactory.javaApplicationContext测试&#xff0c;实现在这里插入图片描述大家好&#xff0c;我是Leo。Spring核心中依赖注入和IOC容器是非…

JWT令牌

1.普通令牌的问题 客户端申请到令牌&#xff0c;接下来客户端携带令牌去访问资源&#xff0c;到资源服务器将会校验令牌的合法性。 从第4步开始说明&#xff1a; 1、客户端携带令牌访问资源服务获取资源。 2、资源服务远程请求认证服务校验令牌的合法性 3、如果令牌合…

QML Image and Text(图像和文字)

Image&#xff08;图片&#xff09; 图像类型显示图像。 格式&#xff1a; Image {source: "资源地址" } source&#xff1a;指定资源的地址 自动检测文件拓展名&#xff1a;source中的URL 指示不存在的本地文件或资源&#xff0c;则 Image 元素会尝试自动检测文件…

MVI 架构更佳实践:支持 LiveData 属性监听

前言MVI架构为了解决MVVM在逻辑复杂时需要写多个LiveData(可变不可变)的问题,使用ViewState对State集中管理&#xff0c;只需要订阅一个 ViewState 便可获取页面的所有状态通过集中管理ViewState&#xff0c;只需对外暴露一个LiveData&#xff0c;解决了MVVM模式下LiveData膨胀…

Linux_vim编辑器入门级详细教程

前言&#xff08;1&#xff09;vim编辑器其实本质上就是对文本进行编辑&#xff0c;比如在.c文件中改写程序&#xff0c;在.txt文件写笔记什么的。一般来说&#xff0c;我们可以在windows上对文本进行编译&#xff0c;然后上传给Linux。但是有时候我们可能只是对文本进行简单的…