这是我参与「第四届青训营」笔记创作的第 14 天
此篇笔记是 Android 基础 —— 6、应用组件Application
本篇笔记分为 2 部分
- Application的生命周期
- 利用Application操作全局变量
六、Application
本节介绍 Android 重要组件 Application 的基本概念和常见用法。
本节介绍 Android 重要组件 Application 的基本概念和常见用法。首先说明 Application 的生命周期贯穿了 App 的整个运行过程,接着利用 Application 实现 App 全局变量的读写,然后阐述了如何借助 App 实例来操作 Room 数据库框架。
Application的生命周期
Application 是 Android 的一大组件,在 App 运行过程中有且仅有一个 Application 对象贯穿应用的整个生命周期。
打开 AndroidManifest.xml ,发现 activity 节点的上级正是 application 节点,不过该节点并未指定 name 属性,此时 App 采用默认的 Application 实例。
注意到每个 activity 节点都指定了 name 属性,譬如常见的 name 属性值为. MainActivity ,让人知晓该 activity 的入口代码是 MainActivity.java 。现在尝试给 application 节点加上 name 属性,看看其庐山真面目,具体步骤说明如下:
(1)打开 AndroidManifest.xml ,给 application 节点加上 name 属性,表示 application 的入口代码是 MainApplication.java。修改后的 application 节点示例如下:
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyDataStorage"
(2)在 Java 代码的包名目录下创建 MainApplication.java ,要求该类继承 Application ,继承之后可供重写的方法主要有以下 3 个。
- onCreate :在 App 启动时调用。
- onTerminate :在 App 终止时调用(按字面意思)。
- onConfigurationChanged :在配置改变时调用,例如从竖屏变为横屏。
光看字面意思的话,与生命周期有关的方法是 onCreate 和 onTerminate ,那么重写这两个方法,并在重写后的方法中打印日志,修改后的 Java 代码如下所示:
// 在 APP 启动时调用
@Override
public void onCreate() {
super.onCreate();
mAPP = this;
Log.d(TAG, "MyApplication onCreate");
}
// 在 APP 终止时调用
@Override
public void onTerminate() {
super.onTerminate();
Log.d(TAG, "onTerminate");
}
(3)运行测试 App ,在 logcat 窗口观察应用日志。但是只在启动一开始看到 MainApplication 的 onCreate 日志(该日志先于 MainActivity 的 onCreate 日志),却始终无法看到它的 onTerminate 日志, 无论是自行退出 App 还是强行杀掉 App ,日志都不会打印 onTerminate 。
无论你怎么折腾,这个 onTerminate 日志都不会出来。Android 明明提供了这个方法,同时提供了关于该方法的解释,说明文字如下:
This method is for use in emulated process environments.It will never be called on a production Android device, where processes are removed by simply killing them; no user code (including this callback) is executed when doing so。
这段话的意思是:该方法供模拟环境使用,它在真机上永远不会被调用,无论是直接杀进程还是代码退出;执行该操作时,不会执行任何用户代码。
现在很明确了,onTerminate 方法就是个摆设,中看不中用。
如果想在 App 退出前回收系统资源,就不能指望 onTerminate 方法的回调了。
利用Application操作全局变量
C/C++有全局变量的概念,因为全局变量保存在内存中,所以操作全局变量就是操作内存,显然内存的读写速度远比读写数据库或读写文件快得多。
所谓全局,指的是其他代码都可以引用该变量,因此全局变量是共享数据和消息传递的好帮手。
不过 Java 没有全局变量的概念,与之比较接近的是类里面的静态成员变量,该变量不但能被外部直接引用,而且它在不同地方引用的值是一样的(前提是在引用期间不 能改动变量值),所以借助静态成员变量也能实现类似全局变量的功能。
根据上一小节的介绍可知,Application 的生命周期覆盖了 App 运行的全过程。不像短暂的 Activity 生命周期,一旦退出该页面,Activity 实例就被销毁。因此,利用 Application 的全生命特性,能够在 Application 实例中保存全局变量。
适合在 Application 中保存的全局变量主要有下面3类数据:
-
会频繁读取的信息,例如用户名、手机号码等。
-
不方便由意容易因频繁分配内存而导致内存泄漏的对象,例如Handler处理器实例等。图传递的数据,例如位图对象、非字符串类型的集合对象等。
-
容易因频繁分配内存而导致内存泄漏的对象,例如Handler处理器实例等。
要想通过Application实现全局内存的读写,得完成以下3项工作:
- 编写一个继承自 Application 的新类 MainApplication 。该类采用单例模式,内部先声明自身类的一个静态成员对象,在创建 App 时把自身赋值给这个静态对象,然后提供该对象的获取方法 getInstance 。 具体实现代码示例如下:
public class MyApplication extends Application {
private static final String TAG = "jzh";
private static MyApplication mAPP;
// 声明一个公共的信息映射对象 可当作全局变量使用
public HashMap<String, String> infoMap = new HashMap<>();
public static MyApplication getInstance() {
return mAPP;
}
// 在 APP 启动时调用
@Override
public void onCreate() {
super.onCreate();
mAPP = this;
Log.d(TAG, "MyApplication onCreate");
}
-
在活动页面代码中调用 MainApplication 的 getInstance 方法,获得它的一个静态对象,再通过该对象访问 MainApplication 的公共变量和公共方法。
-
不要忘了在 AndroidManifest.xml 中注册新定义的 Application 类名,也就是给 application 节点增加 android:name 属性,其值为 .MainApplication 。
然后运行测试 App ,先打开内存写入页面,录入注册信息后保存至全局变量,此时写入界面如图1所示。再打开内存读取页面,App 自动从全局变量获取注册信息,并展示拼接后的信息文本,此时读取界面如图2所示。