Preface
在学习一个 SDK 的时候, 遇到了 Application 类的相关知识, 其实之前也有学习过 Application 类的一些知识, 但是日常开发中使用频率不高, 忘得差不多了. 现在重新来总结下 Application 的使用
英语基础好的可以去参考官方文档
官方文档
下面是官网对 Application 类的简介
Base class for maintaining global application state. You can provide your own implementation by creating a subclass and specifying the fully-qualified name of this subclass as the "android:name" attribute in your AndroidManifest.xml's <application> tag. The Application class, or your subclass of the Application class, is instantiated before any other class when the process for your application/package is created.
大概意思就是 Application 是用来维护全局应用状态的基类. 可以通过自己创建子类并且在 Manifest.xml 文件中通过 name 属性来标记, 用来实现自定义的功能. Application 类将会在任何类之前实例化
继承关系
Application 类继承自 ContextWarpper 类
下面是示意图
特点
单例模式: 每个 App 都有且只有一个 Application 的实例对象 (多进程 App 除外), 可以通过继承 Application 子类来进行自定义, 如果没有自定义的话, App 会在打开是自动创建一个默认的实例对象.
生命周期: App 开启时就会开始实例化 Application 对象, Application 实例的生命周期是最长的, 拥有和 App 一样长的生命周期
获取方式: 如果没有自定义 Application 的话, 同样可以获取到 Application 对象, 使用 Activity.getApplication() 或者 Context.getApplicationContext() 方法都可以获取到对象.
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- // 这两种方法都可以获取到实例对象
- val application = application
- val otherApplication = applicationContext
- }
全局实例: 在不同的组件中 (如: Activity,Service), 都可以获取 Application 对象, 并且都会是同一个对象
方法介绍
onCreate()
调用时间:
当应用打开时, 创建应用程序时调用此方法.
Tip: 这不是 Activity 的 onCreate(), 默认是空实现.
使用方式:
可以用来初始化数据一些全局变量, 对象, 也可以用来做一些环境的配置.
演示:
- class MyApplication: Application() {
- var whatever = "Whatever"
- override fun onCreate() {
- super.onCreate()
- whatever = "Hello"
- //Do something...
- }
- }
注册 ComponentCallback2
简单说明:
上面的示意图中其实还显示了, Application 类是实现了 ComponentCallback2 接口的, 这个接口里面有三个可以覆写的方法, 可以通过 registerComponentCallbacks() 方法来进行注册, 也可以使用 unregisterComponentCallbacks() 来注销.
使用:
- override fun onCreate() {
- super.onCreate()
- registerComponentCallbacks(object : ComponentCallbacks2 {
- override fun onLowMemory() {
- }
- override fun onConfigurationChanged(newConfig: Configuration?) {
- }
- override fun onTrimMemory(level: Int) {
- }
- })
- }
onLowMemory()
这是上面 ComponentCallback2 接口中的方法, 用于在 Android4.0 之前的内存检测, 开发者可以在这个回调方法中做一些优化, 防止被系统杀进程. 目前用这个方法的情景并不多见, 除非开发者想向下兼容的比较深. 目前可以用 onTrimMemory() 替代
调用时间: 监听到系统内存很低的时刻
- registerComponentCallbacks(object : ComponentCallbacks2 {
- override fun onLowMemory() {
- //Do something...
- }
- })
onTrimMemoey()
这个方法目前用来替代上面的 onLowMemory() 方法. 方法会传入一个 level 的 Int 参数, 这是一个目前系统通知给 App 的一个内存不足等级, 越高越严重. 不同的等级系统会对 App 做出不同的操作.
内存不足级别 | 意义 |
---|---|
TRIM_MEMORY_RUNNING_MODERATE | 等级 5: 应用可以正常在前台运行, 但是系统已经要开始杀后台进程了 |
TRIM_MEMORY_RUNNING_LOW | 等级 10: 应用可以正常在前台运行, 但是系统通知释放资源, 不然会影响速度 |
TRIM_MEMORY_RUNNING_CRITICAL | 等级 15: 前台运行, 但是大部分后台被杀死, 此时必须释放内存, 不然此应用进程也会被杀死 |
TRIM_MEMORY_UI_HIDDEN | 等级 20: 系统会将该应用的 UI 资源收回, 转为后台 |
TRIM_MEMORY_BACKGROUND | 等级 40: 此时应用处于 LRU 缓存列表的最近位置, 需要立刻释放容易恢复的资源 |
TRIM_MEMORY_MODERATE | 等级 60: 此时应用处于 LRU 缓存列表的中间位置, 有被杀死进程的危险 |
TRIM_MEMORY_COMPLETE | 等级 80: 非常危险的等级, 应用处于缓存列表最边缘, 即将被杀死 |
系统杀进程规则:
系统会按照 LRU Cache 列表由低到高杀进程, 会优先杀占用内存较高的应用, 也就是应用占用内存较小的话, 被杀死的概率会降低.
使用方法:
- override fun onTrimMemory(level: Int) {
- if (level in TRIM_MEMORY_RUNNING_MODERATE..(TRIM_MEMORY_RUNNING_LOW - 1)){
- //Do something...
- }else if (level>= TRIM_MEMORY_RUNNING_LOW){
- //Do something...
- }
- }
onConfigurationChanged()
作用:
监听 App 的一些配置信息的改变事件 (比如屏幕旋转)
调用时间:
当配置信息改变的时候会回调此方法
配置信息:
配置信息也就是 Manifest.xml 文件中 Activity 中 Android:configChanges 属性的值, 在该属性中填入 Android:configChanges="keyboardHidden|orientation|screenSize" 可以让屏幕旋转时不重启, 而是执行 onConfigurationChanged() 方法
使用方法:
- registerComponentCallbacks(object : ComponentCallbacks2 {
- override fun onConfigurationChanged(newConfig: Configuration?) {
- //Do something
- }
- }
ActivityLifecycleCallbacks()
这是一个接口, 可以通过 registerActivityLifecycleCallbacks() 和 unregisterActivityLifecycleCallbacks() 来注册 / 注销对所有 Activity 生命周期的监听, 当 Activity 的生命周期发生改变的时候, 就会调用接口里的方法.
使用方法
- registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
- override fun onActivityPaused(activity: Activity?) {
- }
- override fun onActivityResumed(activity: Activity?) {
- }
- override fun onActivityStarted(activity: Activity?) {
- }
- override fun onActivityDestroyed(activity: Activity?) {
- }
- override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
- }
- override fun onActivityStopped(activity: Activity?) {
- }
- override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
- }
- })
这个主要就是对不同的生命周期进行监听, 这里就不往里面写操作代码了. 有兴趣的可以自己在里面加 Log 测试.
onTerminate()
在程序结束的时候会调用, 只是用于仿真机的测试, 真机上不会调用, 没什么好说的.
registerOnProvideAssistDataListener()&unregisterOnProvideAssistDataListener()
这两个方法看名字是需要可以注册准备语音助手的数据的监听器, 需要传入 OnProvideAssistDataListener 接口对象, 接口里只有一个 onProvideAssistData(Activity activity,Bundle data) 的方法, 看官网的介绍是当用户启动语音助手时会构建一个 Intent.ACTION_ASSIST. 而且会附加用户请求帮助时的用户位置信息和上下文.
看网上的说法是国外的手机调用语音助手是会调用这个方法, 我猜测应该不是国外手机, 而是谷歌的语音助手, 我用 OxygenOS 系统测试了一下, 发现确实在呼出谷歌助手的时候调用了方法, Bundle 不是空的, 但是我也没找到里面的信息, 应该是我的方法不对, 我目前还没探究出这个方法要怎么用. 如果有知道的大牛麻烦指导一下
我的测试:
- registerOnProvideAssistDataListener { activity, data ->
- if (data == null) {
- Log.d("日志", "Bundle 为空")
- } else {
- var string = "Bundle{"
- for (item in data.keySet()) {
- string += "$item => ${data.get(item)};"
- }
- string += "}Bundle"
- Log.d("日志",string)
- Log.d("日志","data is:$ACTION_ASSIST + $EXTRA_ASSIST_PACKAGE + $EXTRA_ASSIST_CONTEXT")
- startActivity(object :Intent(ACTION_ASSIST){})
- }
- }
测试结果:
日志: Bundle{}Bundle
日志: data is:Android.intent.action.ASSIST + Android.intent.extra.ASSIST_PACKAGE + Android.intent.extra.ASSIST_CONTEXT
自定义 Application
介绍完上面的方法, 现在总算要开始自定义 Application 类了, 其实自定义 Application 类步骤并不多, 下面来介绍一下.
创建继承自 Application 类的子类
这个就没什么说的了, 直接看代码吧
- class MyApplication : Application() {
- override fun onCreate() {
- super.onCreate()
- //DO something...
- }
- }
进入 Manifest 配置自定义类
- <application
- Android:name=".MyApplication"
- </application>
获取 Application 类的实例
获取实例的方法上面已经讲到了, 这里就不赘述了.
作用
一般来说是不需要自定义 Application 类的, 但是如果需要实现下面的功能, 可以自定义 Application 类
初始化部分资源 (全局对象, 全局共享变量, 方法等)
对内存占用进行优化
监听 App 配置信息和所有 Activity 的生命周期
来源: https://juejin.im/post/5bfb55b751882518805ad05a