Jetpack—架构组件—App Startup

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Jetpack—架构组件—App Startup

App Startup


介绍作用

image.png

这是官网的截图,大意就是 App Startup 是一种用来在 app 启动时候规范初始化数据的 library。同时使用 App Startup 可以解决我们平时滥用 ContentProvider 导致的启动变慢问题。

还有一点,App Startup 可以用于 app 开发,也可以用来进行 sdk 开发


App Startup 的优势

  1. 平时使用 ContentProvider 自动获取 ApplicationContext 的方式管理混乱,并且多个 ContentProvider 初始化的方式也无法保证初始化的顺序
  2. 统一管理的方式可以明显提升 app 初始化速度,注:仅限于用较多 ContentProvider 来初始化应用的 app,反之不是不能用,只是没有优化效果


依赖

dependencies {
    implementation("androidx.startup:startup-runtime:1.0.0")
}
复制代码


使用 AppStartup 初始化全局单例对象(main 分支)

  1. Car 对象
class Car(private val name: String) {
    companion object {
        var instance: Car? = null
        fun getInstance(name: String): Car {
            if (instance == null) {
                instance = Car(name)
            }
            return instance!!
        }
    }
    override fun toString(): String {
        return "$name  ${Random.nextInt(100)}"
    }
}
复制代码


  1. 首先需要实现一个 Initializer
class AndroidInitializer : Initializer<Car> {
    override fun create(context: Context): Car {
        return Car.getInstance("出租车")
    }
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return mutableListOf()
    }
}
复制代码


  1. 在代码中注册 AndroidInitializer
<provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
          >
            <meta-data
                android:name="com.ananananzhuo.appstartupdemo.AndroidInitializer"
                android:value="androidx.startup" />
        </provider>
复制代码


  1. 分析

本例中 Car 对象,Car 对象内部维护了一个全局单例方法 getInstance。

前面说了,AppStartup 是用来维护全局单例的,那么实际上这个单例的初始化就是通过我们定义的 AndroidInitializer 对象 create 方法来初始化的。


  1. 我们会在 MainActivity 中调用 Car 的 toString 方法,代码如下
logEE(Car.getInstance("小汽车:").toString())
        logEE(Car.getInstance("小汽车:").toString())
        logEE(Car.getInstance("小汽车:").toString())
复制代码

我们调用了,三次 toString 方法

代码输出如下:

image.png

我们 MainActivity 中代码 getInstance 传入的参数是 "小汽车",但是打印的却是 "出租车"。查看 AndroidInitializer 中的代码发现,我们在 AndroidInitializer 中的 create 方法中创建对象的参数是 "出租车"。

由此可以证明,我们的全局 Car 单例在 AndroidInitializer 中就已经初始化完成了。


手动初始化组件

上一节中我们使用在 Manifest 中注册组件的方式实现 Car 对象的自动初始化。

但是,实际上我们是可以不在 Manifest 中注册的方式实现初始化的,手动初始化的方式如下:

AppInitializer.getInstance(this)
            .initializeComponent(AndroidInitializer::class.java)
复制代码

这种方式的弊端是一次只能初始化一个组件


实现相互依赖的多实例的初始化(分支:multimodule)

通过上一节的学习,你可能会有这样的疑问:AppStartup 啥用没有吧,我直接在 Application 中一行代码初始化不香吗,非要用你这种方式???

那么现在我就要用 AppStartup 实现多实例的初始化,让你进一步了解 AppStartup 的应用


我们这一节的逻辑先描述一下:

本例中我们需要创建两个对象,Person 和 Noodle,两者都是全局单例的。

Person 持有 Noodle 对象的引用,

Person 中有一个 eat 方法,本例中我们的 eat 会输出一行 "某某人" 吃 "面条" 的日志

废话不多说,上代码:

不要嫌代码长,都是一看就懂的逻辑

  1. Person 和 Noodle
class Person(val name:String) {
    private var noodle: Noodle? = null
    companion object {
        private var instance: Person? = null
        fun getInstance(name:String): Person {
            if (instance == null) {
                instance = Person(name)
            }
            return instance!!
        }
    }
    fun addNoodle(paramsnoodle: Noodle) {
        noodle = paramsnoodle
    }
    fun eat() {
        logEE("${name} 吃 ${noodle?.name}")
    }
}
复制代码


class Noodle {
    val name = "面条"
    companion object {
        private var instance: Noodle? = null
        fun getInstance(): Noodle {
            if (instance == null) {
                instance = Noodle()
            }
            return instance!!
        }
    }
}
复制代码


  1. PersonInitializer、NoodleInitializer
class PersonInitializer : Initializer<Person> {
    override fun create(context: Context): Person {
        return Person.getInstance("李白").apply {
            addNoodle(Noodle.getInstance())
        }
    }
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return mutableListOf(NoodleInitializer::class.java)
    }
}
复制代码


class NoodleInitializer:Initializer<Noodle> {
    override fun create(context: Context): Noodle {
        return Noodle.getInstance()
    }
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return mutableListOf()
    }
}
复制代码

这两个组件中 PersonInitializer 的 create 方法中创建了 Person 的实例,并向里面添加 Noodle 的实例。


划重点:

PersonInitializer 的 dependencies 方法中返回了 mutableListOf(NoodleInitializer::class.java)。这句代码的意思是在 PersonInitializer 中的 Person 初始化之前会先初始化 NoodleInitializer 中的 Noodle 实例,然后当 PersonInitializer 中 addNoodle 的时候 Noodle 全局单例已经创建好了。

  1. 调用吃面条方法
Person.getInstance("杜甫").eat()
复制代码
  1. 打印日志输出

image.png

日志输出符合我们的预期

多实例的注册组件方式如下,我们将 PersonInitializer、NoodleInitializer 都被注册到 meta-data 中了。

实际上,NoodleInitializer 的组件是完全可以不注册的,因为在 PersonInitializer 的 dependencies 中已经声明了 NoodleInitializer 组件。

<provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false">
            <meta-data
                android:name="com.ananananzhuo.appstartupdemo.PersonInitializer"
                android:value="androidx.startup" />
            <meta-data
                android:name="com.ananananzhuo.appstartupdemo.NoodleInitializer"
                android:value="androidx.startup" />
        </provider>
复制代码


使用 AppStartup 进行 sdk 开发(分支:sdk_develop)

本例介绍 sdk 开发中 AppStartup 的使用,实际上与应用开发是一样的,但是感觉还是有必要说一下。

在本例中我们新建了一个 library 的 module,在 library 里面编写了我们的 AppStartup 的代码逻辑,然后将 Library 打包成 arr,集成到 app 模块中,在 app 的 Manifest 中注册组件,并调用组件的相关方法。

  1. aar 集成

image.png

  1. library 中的代码
class LibraryInitializer:Initializer<Student> {
    override fun create(context: Context): Student {
        return Student.getInstance()
    }
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
       return mutableListOf()
    }
}
复制代码


class Student(val name: String) {
    companion object {
        private val student = Student("安安安安卓")
        fun getInstance(): Student {
            return student
        }
    }
    fun study() {
        Log.e("tag", "${name}  好好学习")
    }
}
复制代码


  1. Manifest 中注册组件
<provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="com.ananananzhuo.appstartupdemo.androidx-startup"
            android:exported="false"
          >
            <meta-data
                android:name="com.ananananzhuo.library.LibraryInitializer"
                android:value="androidx.startup" />
        </provider>
复制代码


  1. 日志打印

image.png


  1. 结论

通过这种方式,第三方 sdk 只需要定义自己的 AppStartup 组件就可以,我们在注册组件的时候在 manifest 中添加第三方组件的信息就可以完成第三方组件的初始化了。

这极大的避免了某些自以为是的 sdk,打着方便我们集成的名义搞 ContentProvider 初始化恶心我们

以后如果你合作的第三方 sdk 提供方再出现 ContentProvider 的初始化方式恶心你,那么拿出我的文章好好教他做人。



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
16天前
|
负载均衡 5G 网络性能优化
深入解析LTE(长期演进技术)的基本架构及其关键组件
深入解析LTE(长期演进技术)的基本架构及其关键组件
80 2
|
2月前
|
Kubernetes API 调度
Kubernetes 架构解析:理解其核心组件
【8月更文第29天】Kubernetes(简称 K8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用。它提供了一个可移植、可扩展的环境来运行分布式系统。本文将深入探讨 Kubernetes 的架构设计,包括其核心组件如何协同工作以实现这些功能。
102 0
|
22天前
|
移动开发 小程序 前端开发
uni-app组件样式修改不生效
uni-app组件样式修改不生效
|
27天前
|
XML Java 数据库
在微服务架构中,请求常跨越多个服务,涉及多组件交互,问题定位因此变得复杂
【9月更文挑战第8天】在微服务架构中,请求常跨越多个服务,涉及多组件交互,问题定位因此变得复杂。日志作为系统行为的第一手资料,传统记录方式因缺乏全局视角而难以满足跨服务追踪需求。本文通过一个电商系统的案例,介绍如何在Spring Boot应用中手动实现日志链路追踪,提升调试效率。我们生成并传递唯一追踪ID,确保日志记录包含该ID,即使日志分散也能串联。示例代码展示了使用过滤器设置追踪ID,并在日志记录及配置中自动包含该ID。这种方法不仅简化了问题定位,还具有良好的扩展性,适用于各种基于Spring Boot的微服务架构。
31 3
|
2月前
|
存储 安全 虚拟化
深入解析:Docker的架构与组件
【8月更文挑战第27天】
132 5
|
2月前
|
JSON 前端开发 API
Django 后端架构开发:通用表单视图、组件对接、验证机制和组件开发
Django 后端架构开发:通用表单视图、组件对接、验证机制和组件开发
47 2
|
2月前
|
XML 数据格式
【Azure Logic App】在Logic App中使用 Transfer XML组件遇见错误 undefined
【Azure Logic App】在Logic App中使用 Transfer XML组件遇见错误 undefined
|
2月前
|
消息中间件 运维 NoSQL
基础架构组件选型及服务化
【8月更文挑战第22天】本文概述了分布式系统中常见的基础架构组件及其选型与服务化的重要性。
|
2月前
|
开发工具 Android开发
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
32 0
下一篇
无影云桌面