美文网首页基础android基础源码分析
Android 基础之 Activity 面面观

Android 基础之 Activity 面面观

作者: 秀花123 | 来源:发表于2017-05-14 23:09 被阅读411次

    一、生命周期

    生命周期

    上图是 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 的 <activity> 标签在属性 android:configChanges 中声明,属性取值如下:


    然后在 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 文件中声明的 <intent-filter> 来寻找合适的组件,如果匹配则启动该组件,如果存在多个满足条件的组件,则弹出 Dialog 进行选择。

    Intent 对象的构成

    1. ComponentName

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

    启动 Service 必须显式启动。

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

    2. Action

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

    预定义的启动 Activity 的 Action
    预定义的发送 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 项;

    预定义 Category
    6. Extras

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

    7.Flags

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

    IntentFilter

    通过 manifest 文件中包含在 <activity><activity-alias><service><receiver> 中的 <intent-filter> 标签定义:

    Intent 的组成
    1. <action>:必须项,可以包含多个。可以通过 IntentFilter#addAction 动态添加;
    2. <category>:可选项,可以包含多个。只要 Intent 中的 Category 满足其中一个,就可以接受该 Intent 的请求,可以通过 IntentFilter#addCategory 动态添加;
    3. <data>:可选项,描述可接受的数据范围和类型
    <data android:scheme="string"// URI 的 scheme 部分
         android:host="string"  //URI 对应的域名信息
          android:port="string" 
          android:path="string" //完整路径信息
          android:pathPattern="string" //路径信息前缀
          android:pathPrefix="string"  //模糊匹配
          android:mimeType="string" //对应 Intent 的 Type,表示可以接受的数据类型
    />
    

    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,可以通过 <action> 标签中的 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 属性:
      <item name="android:windowAnimationStyle">@style/activityAnim</item>
      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 目录下,然后通过上述属性 <item>在 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 对象,

    Transition 直接子类和间接子类
    这里简单列一下几个间接子类
    • Explode: 爆炸效果,xml 中使用 <explode> 标签;
    • Slide:滑动效果,xml 中使用 <slide> 标签;
    • Fade:淡入淡出效果, xml 中使用 <fade> 标签;

    相关文章

      网友评论

      • utf8_1436:我可以借鉴你的说法然后写一篇相关的总结么
        utf8_1436: @墨眉无锋 好哒
        秀花123:@utf8_1436 可以,我也是借鉴了很多别人的东西
      • utf8_1436:受益匪浅

      本文标题:Android 基础之 Activity 面面观

      本文链接:https://www.haomeiwen.com/subject/xfzgtxtx.html