上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面。主Activity的确定规则在Android系统编程入门系列之清单文件有介绍,本文主要介绍Android系统创建Activity之后的生命周期流程。

在清单文件中所注册的界面均为自定义Activity,其父类往上追溯,必须继承自android.content.Activity

生命周期

Activity作为四大组件之首,主要负责与系统使用者的可视化交互响应。只有深刻掌握Activity的生命周期及相关概念,才能在开发设计时游刃有余。注意,这里的生命周期介绍,与官方生命周期定义有所区别,本文中的范围更加宽泛。

Android组件的生命周期,均是由Android系统主线程调用,如果在调用的生命周期方法内出现耗时操作,将会导致后续的生命周期方法无法被及时调用,反应到交互界面上就是应用程序卡顿甚至操作无响应。为了防止这种情况的发生,Android系统定义当应用程序超时无响应时间超过一定时长(界面Activity默认5秒),会触发应用无响应错误ANR,同时界面弹出提示对话框,以供用户选择退出停止响应或继续等待。所以在生命周期方法内不允许耗时操作。

(调用构造方法)启动实例化

界面Activity的启动需要通过android.content.Intent意图来操作,意图的创建分显示意图和隐示意图两种类型。顾名思义,显示意图要指定Activity的具体包名和类名,而隐示意图只需要指定Activity在清单文件中注册时所嵌入的actioncategory标签信息。创建的意图作为参数才能启动界面Activity。显示意图常用于当前应用内的界面Activity之间启动,而隐示意图多用于不同应用间的界面启动。

回想下,在Android系统编程入门系列之清单文件文章中说到,主Activity的注册时,必须在其标签内部嵌入<intent-filter></intent-filter>标签,并在该标签内固定且唯一<action><category>标签的内容。这种写法的原因,就在于Android系统是创建的隐示意图启动应用内的主Activity。举一反三,留个问题思考下,如果一个应用的清单文件中有多个上述主Activity的固定<intent-filter></intent-filter>标签内容,那Android系统在启动这个应用后会怎么调用这些Activity呢?(答案将在相应视频教程中揭晓)

在Android系统通过清单文件找到意图指定的界面Activity之后,会实例化该界面Activity对象,并将其放入当前应用程序的指定任务栈中。任务栈,正如其名,是采用先进后出的模式管理当前应用程序启动的所有界面Activity对象的数据结构。

当界面Activity启动时放入任务栈中,退出界面Activity时则从任务栈中取出其对象并销毁。然而如果一个应用程序只对应一个任务栈,在有些频繁启动退出单个界面的场景中,就会频繁创建销毁该界面实例,浪费了很多cpu耗时。为了优化界面的实例化过程,Android系统允许一个应用程序使用多个任务栈。这也就引出了两个问题,一是如何从多个任务栈中指定具体一个作为界面启动管理的任务栈?二是界面在启动时放入不同任务栈的规则是什么?

对于第一个问题,在清单文件中静态注册界面Activity时,可以在<activity></activity>标签中对属性名taskAffinity赋值,该属性值默认为当前应用包名,从而指定当前界面Activity所属的任务栈。而<application></application>标签中也可以使用该属性,表示当前应用程序内的所有界面启动时都使用该属性值对应的任务栈管理。同时在界面Activity中,可以使用getTaskId()来获取当前界面Activity所属任务栈对应的id值。

对于第二个问题,可以根据界面Activity启动模式来确定放入任务栈的规则。启动模式可以在清单文件中静态注册<activity></activity>标签时,指定其属性launchMode赋值;也可以在创建意图Intent操作之后,通过调用其setFlags(int flags)方法动态设置。如果动态设置方式会优先覆盖静态设置方式,如果不设置启动模式,则使用默认模式启动。启动模式主要有四种,其具体方式和对应的属性值可参考下表。

启动模式 静态设置 动态设置 功能含义
标准模式(默认模式) standard - 启动时会在内存空间中实例化对象,并将该对象放入指定任务栈中
栈顶单例模式 singleTop Intent.FLAG_ACTIVITY_SINGLE_TOP 启动时先检查指定任务栈顶部界面对象,
如果与该界面信息一致,则使用任务栈顶层的对象;
否则实例化对象并放入指定任务栈顶部
栈内单例模式 singleTask Intent.FLAG_ACTIVITY_CLEAR_TOP 启动时检查指定任务栈内的所有界面对象,
如果存在与该界面信息一致的对象,则将其顶部界面对象移出任务栈并销毁,最终保留一致对象在任务栈顶部;
否则实例化对象并放入指定任务栈顶部
全局单例模式 singleInstance Intent.FLAG_ACTIVITY_NEW_TASK 启动时检查当前应用程序使用的所有任务栈内的所有界面对象,
如果存在与该界面信息一致的对象,则将其所在任务栈顶部的界面对象移出任务栈并销毁,最终保留一致对象在任务栈顶部;
否则实例化对象并放入指定任务栈顶部

在界面`Activity`启动实例化阶段,只是创建实例,并未加载Android系统相关环境,故该阶段一般不需要重写构造方法。

(调用attachBaseContext(Context base))加载运行环境

Application一样,Activity也继承自android.content.ContentWrapper,使用时绑定上下文环境,尤为注意的是,在AndroidSDK定义的所有android.content.ContentWrapper子类中,只有android.content.Activity类中重写了attachBaseContext(Context base)方法。故界面Activity实例化之后,系统会最终调用android.content.Activity.attachBaseContext(Context base)方法,在该方法中以完成将当前界面与上下文环境绑定的任务。

同样的,在界面Activity加载运行环境阶段,也不推荐重写该方法,界面内所有的操作都应在该方法之后完成。

(调用onCreate())界面创建

在界面加载运行环境之后,Android系统对界面的创建工作就完成了。之后系统回调onCreate()方法,官方称之为已创建状态。此时当前Activity还处在后台未展示给用户,因此可以重写该方法,以完成界面内相关变量的初始化操作。

(调用onStart())界面展示

在界面创建之后,或界面重新展示之后,系统会回调onStart()方法,将当前Activity绘制给用户可见,官方称之为已开始状态。如果重写该方法,可以完成展示给用户的界面初始化操作。

(调用onResume())界面运行

当界面展示之后,系统会回调onResume()方法,使得界面可以与用户进行交互,官方称之为已恢复状态。此时系统开始响应该界面内的用户交互,这也表明当前界面Activity已经处于运行了。

系统调用该生命周期方法后会一直处于界面运行中,直到用户的一些操作改变该界面的状态。这些用户操作包括但不限于:

  1. 在该界面Activity启动另一个界面Activity
  2. 点击back返回按键,退出该界面Activity,返回上一个界面Activity
  3. 点击home主页按键,该应用Application切换到后台。
  4. 点击menu菜单按键,该应用的当前界面Activity在最近任务栏展示。
  5. 点击分屏按键,该应用Application作为多窗口模式之一显示。
  6. 点击息屏按键,该应用Application在后台休眠。
  7. 点击电源按键,强制关机,该应用Application被杀死。

界面暂停(调用onPause()

当界面运行状态被改变后,系统都会首先调用onPause()方法,此时系统已停止当前界面Activity与用户的交互响应操作,官方称之为已暂停状态。可以重写该方法,在其中做释放一些不需要但却很耗电的系统资源,以防止无效持有消耗电量。但是此时该界面仍然对用户可见,所以不建议在该方法内执行耗时的释放操作,以免给用户带来卡顿的假象。

在该状态之后,Android系统根据之前的用户操作类型判断后续生命周期流程。

操作1、2、3、6、7可能会继续令当前界面Activity停止展示

而操作4、5将可能在用户重新点击该界面返回,此时将重新回到界面运行的生命周期中。

界面停止展示(调用onStop()

当系统令当前界面Activiy停止展示时,会调用onStop()方法,将当前界面对用户不可见,官方称之为已停止状态。重写该方法时,可以释放界面资源和不需要的CPU耗时资源,以供其他地方及时获得。

在该状态之后,Android系统继续根据之前的用户操作类型判断后续的生命周期流程。

操作1会将新启动的界面Activity放入当前Activity所在任务栈中,等待新启动的界面返回时,令当前界面重新展示

操作2则是将当前界面从任务栈中取出并销毁

操作3则可能在手机内存不足时,将当前应用的所有界面包括当前界面从任务栈中取出并销毁

操作6会将当前应用休眠,等设备重新亮屏后,令当前界面重新展示;也可能有的设备在息屏休眠后保持低消耗电源模式时,当前应用超过一定时间或内存占用的休眠期后,被系统杀死,即当前应用的所有界面包括当前界面从任务栈汇总取出并销毁

操作7则会将当前应用的所有界面全部销毁,不会再调用当前界面的任何生命周期。

界面重新展示(调用onRestart()

当界面由用户不可见转为可见时,如果之前已经界面创建了,则会调用onRestart()方法,以表明当前界面将会重新展示。重写该方法可以处理对用户重新可见后的操作。

该方法调用之后,系统将会继续调用界面展示运行,以重新运行界面与用户的交互操作。

界面销毁(调用onDestroy()

当界面Activity被移除任务栈后,如果应用程序还处在Android系统的控制下,系统将在调用onDestroy()方法之后销毁界面,官方称之为已销毁状态。重写此方法可以释放所有不需要的资源,以防止发生内存泄漏OOM的问题。


上述内容是针对单个界面Activity的加载流程,那么其中涉及到的两个界面Activity的相互交流方式是怎样的,以及界面运行之后如何处理与用户的交互操作?详情请关注后续文章。

Android系统编程入门系列之加载界面Activity的更多相关文章

  1. Android系统编程入门系列之加载服务Service

    之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始 ...

  2. Android系统编程入门系列之界面Activity绘制展示

    上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...

  3. Android系统编程入门系列之界面Activity交互响应

    在上篇文章中已经了解到界面Activity的绘制完全依赖其加载的视图组件View,不仅如此,用户的每次触摸操作都可以在界面Activity内接收并响应,也可以直接传递给其中的某个视图View响应.本文 ...

  4. Android系统编程入门系列之界面Activity响应丝滑的传统动画

    上篇文章介绍了应用程序内对用户操作响应的相关方法位置,简单的响应逻辑可以是从一个界面Activity跳转到另一个界面Activity,也可以是某些视图View的相对变化.然而不管是启动一个界面执行新界 ...

  5. Android系统编程入门系列之应用初始化Application

    在上一篇文章中我们了解到Android系统启动应用的时候,会首先加载AndroidManifest.xml清单文件中的一系列信息,在清单文件中如果不指定<application></ ...

  6. Android系统编程入门系列之清单文件

    在上一篇文章中已经提到,Android系统加载应用程序之后,首先会读取该应用程序的AndroidManifest.xml清单文件,之后根据该清单文件加载后边的东西.所以要开发应用程序,自然要先知道清单 ...

  7. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  8. Android系统编程入门系列之界面Activity响应多元的属性动画

    在响应丝滑动画一篇文章中,分别介绍了作用于普通视图.绘制视图的绘制对象.和界面这三种对象的动画效果,但是都有一些使用的局限性.比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的 ...

  9. Android系统编程入门系列之服务Service中的进程间通信

    在上篇文章以线程间的通信方式Handler类结尾,服务Service还支持的进程间通信,又是具体怎么实现的呢?这就要用到加载服务一文中提到的AIDL语言规范了. AIDL是 Android Inter ...

随机推荐

  1. String相关介绍

    String 字符串是常量,创建后不可改变. 字符串字面值存储在字符串池中,可以共享. String s1 = "Runoob"; // String 直接创建 String s2 ...

  2. [bug] Flask css 不更新

    参考 https://blog.csdn.net/weixin_30454481/article/details/97108510

  3. 把一个整体目标设置成多个分阶段目标,完成了一个目标后,就相当于一件事OVER

    如果事情有变坏的可能,不管这种可能性有多小,它总会发生 . 一.任何事都没有表面看起来那么简单:二.所有的事都会比你预计的时间长:三.会出错的事总会出错:四.如果你担心某种情况发生,那么它就一定会发生 ...

  4. 实例:使用playbook实现httpd安装、配置、以及虚拟主机的配置

    一.安装环境配置 1.在控制节点给受控主机配置本地仓库文件 [root@ansible ~]# vim /etc/yum.repos.d/dvd.repo [AppStream] name=appst ...

  5. C++知识点案例 笔记-3

    1.基类指针等与派生类的兼容 2.构造函数 3.析构函数 4.虚基类 5.虚函数 6.虚析构函数 ==基类指针等与派生类的兼容== #include <iostream> #include ...

  6. 【转载】java与xml

    原文地址:http://www.lai18.com/content/1198237.html java项目中,xml文件一般都是用来存储一些配置信息一般的编程, 多数用来存储配置信息 . 拿JDBC来 ...

  7. 「 刘一哥与GIS的故事 」专业技术博文专栏目录索引

    刘一哥,多年研究地图学.地理信息系统.遥感.摄影测量和GPS等应用,精通ArcGIS.MapGIS.ENVI.Erdas.CASS.Pix4d.CC.PhotoScan.Inpho.EPS.Globa ...

  8. EVE-NG 入门教程 (Mac OSX BIg Sur)

    EVE-NG 入门搭建指南 1. 什么是 EVE-NG EVE-NG (Emulate d Virtual Environment) 是一个可以运行虚拟主机.网络设备的模拟器,目前该软件分为了社区版. ...

  9. Angel图算法

    Angel图算法 [2.0]CommonFriends 计算两个好友的共同好友数,某种程度上可以刻画两个节点之间的紧密程度. 输入 输入数据路径:输入文件所在路径,无权网络数据, 数据格式为两列 sr ...

  10. AI框架外部用户贡献代码

    AI框架外部用户贡献代码 概述 飞桨是百度自主研发的一款开源的深度学习框架,是主流深度学习框架中首个完全国产化的产品,已经在农业.医疗.林业.科研.服务等领域成功应用.无论是已入职场的深度学习从业者. ...