Android 基础之 Activity 面面观

一、生命周期

Android 基础之 Activity 面面观_第1张图片
生命周期

上图是 Activity 和 Fragment 的完整的生命周期函数调用过程,Activity 常规的生命周期回调函数有七个:

  1. onCreate:Activity 第一次创建时调用,一般在该函数中做一些初始化操作,比如创建 View,绑定数据到 View 等。该函数有一个 Bundle 类型的参数 onSaveInstanceState 用于 Activity 被系统销毁后重建;
  2. onStart:Activity 变为可见状态。可以在该方法中注册 BroadcastReceiver;
  3. onResume:Activity 变为可交互(前台)状态。此时 Activity 处于所有 Activivty 的最前端,一个 Activity 可能会频繁的在前台状态和可见状态切换,比如弹出 Dialog 和锁屏;
  4. onPause:Activity 由可交互状态变为部分可见状态。因为在系统资源不足的情况下,会直接杀死 Activity 而不会调用后面的生命周期方法,所以可以在该方法中存储一些变化的数据,或者停止一些不该在可见状态执行的操作,如停止动画,暂停视频等,但是为了保证及时切换到下一个 Activity,不要在该方法内做重量级的操作;
  5. onStop:Activity 变为不可见(后台)状态。可以在该方法中反注册 BroadcastReceiver 以及一些不适合在 onPause 执行的重量级操作;
  6. onDestroy:销毁 Activity。在该方法中执行释放资源,终止线程等操作;即使一个 Activity 被销毁其中的 static 变量还是存在于内存中的,因为 static 是全局变量且生命周期在应用进程的生命周期结束时才结束;
  7. onRestart:Activity 调用 onStop 方法切换到后台后再重新启动不需要调用 onCreate 方法,而是调用 onRestart 方法;

常见场景的生命周期

  1. Activity 正常启动
onCreate --> onStart --> onResume
  1. 按 Back 键退出
onPause --> onStop --> onDestroy
  1. 从 A Activity 跳转到 B Activity
A#onPause -- > B#onCreate -- > B#onStart --> B#onResume --> A#onStop
  1. 从 B Activity 返回 A Activity
B#onPause -- > A#onRestart --> A#onStart --> A#onResume --> B#onStop --> B#onDestroy
  1. 按 Home 键回到主屏
onPause --> onStop
  1. 从主屏返回
onRestart --> onStart --> onResume
  1. 弹出 Dialog 或锁屏
onPause 
  1. 关闭 Dialog 或解锁屏
onResume

二、状态恢复和保存

Activity 的销毁份两种情况:一种是调用 finish() 方法主动退出,另一种是被系统销毁,在系统销毁 Activity 时会调用 onSaveInstanceState(Bundle outState) 方法保存状态,并在重建 Activity 时调用 onRestoreInstanceState(Bundle savedInstanceState) 方法恢复状态,主动退出 Activity 则不会。
而系统销毁 Activity 的情景可以分两种:

  1. 系统配置发生变化
    当系统配置发生变化时,会销毁 Activity 并立即重启:回调函数调用如下:
onPause --> onSaveInstanceState --> onStop --> onDestroy --> onCreate --> onStart --> onRetainInstanceState --> onResume

如果不想让 Activity 重建,可以在 AndroidManifest.xml 的 标签在属性 android:configChanges 中声明,属性取值如下:

Android 基础之 Activity 面面观_第2张图片
Android 基础之 Activity 面面观_第3张图片

然后在 Activity#onConfigurationChanged() 方法中处理配置的变化。
比较常处理的配置有:

  • locale:系统语言切换
  • fontScale:字号发生改变
  • keyboard:键盘类型发生改变
  • orientation:屏幕方向发生变化,API13以上要和 screenSize 一起使用
  1. 处于后台(调用 onStop 方法)或者暂停(调用 onPause 方法)状态的 Activity 因为系统资源不足而被杀死。
    这种情况下,在 onStop 方法前会调用 onSaveInstanceState 方法保存状态,待系统资源充足后会重新创建 Activity,并在 onStart 方法之后调用 onRetainInstanceState 方法恢复状态,也可在 onCreate 方法中调用参数 onSaveInstanceState 恢复状态。

在 Activity#onSaveInstanceState 被调用时,Activity 将会从 View 层次(View Hierachy)中自动搜集每一个 View 的状态,所搜集的 View 需要满足两个条件:

  • View 实现了 onSaveInstanceStateonRetainInstanceState 方法;
  • View 设置了 android:id 属性;

所以我们保存和恢复 Activity 状态时一般不必对 View 进行处理,但是 Activity 的成员变量会和 Activity 一起销毁,需要手动恢复和保存。
Android 提供的标准 View 组件基本上都实现了状态的保存和恢复,只是有些需要我们手动让它生效。比如为 TextView 设置 android:freezeText="true"

三、运行模式

Android 中引入了 Task 的概念,由一组为了完成某项工作而聚集在一起的 Activity 对象共同组成,它不受应用和进程的约束,Task 中的 Activity 是按照 Stack 的形式组织的,成为 Activity Stack。栈底 Activity 是整个任务的发起者,栈顶 Activity 是该任务与用户正在交互的 Activity,栈顶 Activity 执行完成后会退栈销毁。

运行模式

开发者可以通过 android:launchMode 属性来改变该 Activity 的运行模式,在不同的运行模式下,Activity 的任务组件栈会有所变化:

  1. standard(默认模式)
    每次启动 Activity 都会创建一个新的 Activity 实例。
  2. singTop(栈顶复用模式)
    如果要启动的 Activity 实例已经存在并且位于栈顶,而是直接复用,将调用者发出的 Intent 对象通过 Acitvity#onNewIntent 方法传递给栈顶的 Activity 对象。
    singTop 模式适用于与用户交互时保持信息更新的 Activity;
  3. singTask(栈内复用)
    如果要启动的 Activity 已存在于栈内,那么不再创建新的实例,而是将该 Acitivity 上面所有的 Activity 出栈销毁,并调用 onNewIntent 方法。
    栈内只存在一个 Activity 的实例,并且可能会发生任务栈的切换,一定会跳转到目标 Activity 所在的栈中进行,与调用者没有任何关系,而 standard 和 singTop 模式不会。
  4. singInstance(单例模式)
    该模式也在内存中也只有一个 Activity 实例存在,通过 onNewIntent 方法处理 Intent 对象,与 singTask 不同的是,其所在的任务栈中只有一个 Activity 对象。
    singTask 和 singInstance 模式适用于消耗内存较多的单实例 Activity,比如浏览器界面,音乐播放器界面等。

Intent flags 设置启动模式

  • FLAG_ACTIVITY_SINGLE_TOP:相当于 singTop 启动模式;
  • FLAG_ACTIVITY_CLEAR_TOP:相当于 singTask 模式;
  • FLAG_ACTIVITY_NEW_TASK:在一个新的任务栈中启动 Activity,如果 Activity 的 android:taskAffinity 属性已被设置,会先寻找具有同样任务名的 task 是否已存在,如果已存在就不再构造新的任务栈。

具有相同 android:taskAffinity 属性值的 Activity 属于同一个任务栈,Activity 默认在以应用包名为名字的任务栈中

四、Intent 和 IntentFilter

Intent 的作用

  1. 启动 Activity
  2. 启动 Service
  3. 发送 Broadcast

Intent 对象的分类

  1. 显式 Intent: 指定组件的类名,通常用于启动自己应用内的组件;
  2. 隐式 Intent:通过指定 Action 来指定要启动的组件,通常用于启动其它应用的组件;
    当使用隐式 Intent 时,Android 系统通过比较 Intent 的内容和 AndroidManifest.xml 文件中声明的 来寻找合适的组件,如果匹配则启动该组件,如果存在多个满足条件的组件,则弹出 Dialog 进行选择。

Intent 对象的构成

1. ComponentName

组件名字,可选项。如果没有 ComponentName 则只能通过其它 Intent 信息(action、data、category)来隐式启动组件。

启动 Service 必须显式启动。

Intent 的该属性是一个指定了目标组件类名的 ComponentName 对象,可以通过 setComponentsetClasssetClassName 或者 Intent 的构造方法来设置;

2. Action

指定了要执行动作的字符串,Intent 类里面定义了很多 Action 常量,我们也可以自定义 Action,需要注意但是要以包名作为前缀,可以通过 setAction 或者 Intent 构造方法设置;

Android 基础之 Activity 面面观_第4张图片
预定义的启动 Activity 的 Action

Android 基础之 Activity 面面观_第5张图片
预定义的发送 Broadcast 的 Action

3.Data

指定 action 要操作的 data 的 URI,URI 能够表达存储在任何地方的数据,比如

  • 位于本地目录/sdcard/下的 example.data 文件
    file:///sdcard/sample.data
  • 数据源组件 com.duguhome.providers.sample 中 id 为 1 的数据
    content://com.duguhome.providers.sample/1
  • 存放在 Web 的数据
    http://flyvenus.net/sample.data

可以通过 setData 或者 setDataAndType 进行设置

4. Type

MIME 格式的字符串,用于描述组件能够处理的处理的请求类型,或者补充说明 Data 数据的类型,它可以通过通配符来表示整个类别的信息,比如 image/* 也可以更具体地指定子类别 image/jpg,可以通过 setType 设置, setDatasetType 是互相排斥的,如果需要同时指定,需要使用 setDataAndType 方法;

5. Category

包含了要处理该 Intent 的组件类别的额外信息,Intent 类里面也预定义了很多的 Category 常量,比如 CATEGORY_DEFAULTCATEGORY_LAUNCHER, 也可自定义 Category 项,同样需要依赖于包名,可以通过 addCategory 方法为 Intent 添加 Category 项;

Android 基础之 Activity 面面观_第6张图片
预定义 Category

6. Extras

用于组件间传输数据的 Bundle 类型的键值对,实现了 Parcelable 接口,通过 putExtra(key,value) 系列方法或者 putExtras(Bundle) 方法设置 Extra,通过 getXXXExtra 方法获取;
一般少量的数据用 Extras 传输,大量的数据用 Data 传输;

7.Flags

Intent 类里定义的整型常量,通过 setFlags 方法设置;

IntentFilter

通过 manifest 文件中包含在 中的 标签定义:

Intent 的组成
  1. :必须项,可以包含多个。可以通过 IntentFilter#addAction 动态添加;
  2. :可选项,可以包含多个。只要 Intent 中的 Category 满足其中一个,就可以接受该 Intent 的请求,可以通过 IntentFilter#addCategory 动态添加;
  3. :可选项,描述可接受的数据范围和类型

Intent 匹配流程

1)比较 Action:如果 Intent 包含 Action 信息,就必须要求该 Action 项在 IntentFilter 的 Action 列表中;
2)比较 Data 和 Type:如果 Intent 中不包含 Data 项和 Type 项,IntentFilter 也不能包含相关信息,如果包含 Type 项,则要求 IntentFilter 的 Type 信息基于通配符 * 比较下相等,如果 Intent 包含 Data 项,则拆分成 Scheme 和 Authority 逐一比较,必须完全匹配;
3)比较 Category:如果 Category 不包含任何 Category 项,则直接匹配成功,如果包含 Category 项,则要求 Intent 的所有 Category 项都出现在 IntentFilter 的 Category 列表中;

在进行 Activity 调用时,如果 Intent 对象没有添加 Category 项,系统会为其添加上 Intent.CATEGORY_DEFAULT 类别,所以 Activity 要想作为通用的功能组件被调用,必须显性地添加 Intent.CATEHORY_DEFAULT
如果有多个 IntentFilter 与 Intent 相匹配,会基于优先级进行选择,每个 IntentFIlter 都有一个优先级,其范围从-1000到1000,默认为0,可以通过 标签中的 android:priority 属性或者 setPriority 方法设置,如果优先级一致,按照组件的名字字母排序进行调用

五、转场动画

Activity 有默认的切换效果,也可以进行自定义,一般有如下几种方式:

  1. Activity#overridePendingTransition(int enterAnim, int exitAnim)
    该方法必须在 startActivity 或者 finish 方法之后调用才能生效,
  • exterAnim:Activity 进入时的动画资源 id;
  • exitAnim:Activity 退出时的动画资源 id;
  1. 在 Activity 的 theme 里面定义:
    在 style 中定义 android:windowAnimationStyle 属性:
    @style/activityAnim
    windowAnimationStyle 中包含4中动画:
  • android:activityOpenEnterAnimation
  • android:activityOpenExitAnimation
  • android:activityCloseEnterAnimation
  • android:activityCloseExitAnimation
  1. 使用 windowEnterAnimationwindowExitAnimation
    方法使用的是 activityXXX 属性,这里使用的是 windowXXX 属性
  2. 使用 Window 的 windowXXXTransition 属性
  • android:windowEnterTransition:第一次进入时的动画,可以调用方法 Window#setEnterTransition(Transition) 方法设置;
  • android:windowExitTransition:退出时的动画,可以调用方法 Window#setEixtTransition(Transition) 方法设置;
  • android:windowReenterTransition:再次进入时的动画,可以调用方法 Window#setReeterTransition(Transition) 方法设置;
  • android:windowReturnTransition:返回时的动画,可以调用方法 Window#setReturnTransition(Transition) 方法设置;

Transition 类

Transiton 可以通过 xml 文件定义,放在 res/transition 目录下,然后通过上述属性 在 style 文件中设置。
在代码中设置需要一下几步:
1)在
setContentView 之前设置
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 告诉 Window 页面切换需要使用动画;
2)使用 TransitionInflater 加载动画 Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
3)getWindow().setWindowXXXTransition 方法
4)调用 startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) 方法跳转,第二个参数是一个 Bundle 对象,

Android 基础之 Activity 面面观_第7张图片
Transition 直接子类和间接子类

这里简单列一下几个间接子类

  • Explode: 爆炸效果,xml 中使用 标签;
  • Slide:滑动效果,xml 中使用 标签;
  • Fade:淡入淡出效果, xml 中使用 标签;

你可能感兴趣的:(Android 基础之 Activity 面面观)