四大组件之全面了解Activity

Activity 的概念

Activity是应用与用户交互的入口点,它提供窗口供应用在其中绘制UI,这个窗口可能填满整个屏幕,也可能小于屏幕悬浮在其他窗口之上。通常来说一个应用会存在多个Activity,但只会存在一个主Activity(用户启动应用时显示的第一个Activity),然后每个Activity都可以启动另外的Activity以组成多屏幕的应用。

配置清单

如果要使用Activity,我们必须在AndroidManifest.xmk清单中配置,如下

xml 复制代码
<!--主Activity-->
<activity
    android:name=".activity.MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<!--正常Activity-->
<activity
    android:name=".activity.SecondActivity"
    android:exported="true">
</activity>

Activity启动

Activity的启动由Intent触发,其中Intent又分为显示Intent和隐式Intent,显示Intent可以明确的指向某一个Activity,隐式Intent则指向一个或多个Activity。一般来讲一个Intent不应该既是显示又是隐式,如果二者共存以显示为主。

显示Intent

属于精确匹配,有以下三种构建方式

1. 在Intent构造函数中指定

kotlin 复制代码
startActivity(Intent(this@MainActivity,SecondActivity::class.java))

2. 调用意图对象的setClass方法指定

kotlin 复制代码
   val intent = Intent()
   intent.setClass(this@MainActivity,SecondActivity::class.java)
   startActivity(intent)

3. 调用意图对象的setComponent方法指定

kotlin 复制代码
 val intent = Intent()
 val componentName  = ComponentName(this@MainActivity,SecondActivity::class.java)
 intent.setComponent(componentName)

隐式Intent

没有明确指定要跳转的目标Activity,通过IntentFilter匹配规则来指定跳转的Avtivity,过滤信息有action、category和data,如下所示是一个例子

xml 复制代码
<activity
    android:name=".activity.MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="com.abiao.action.a" />
        <action android:name="com.abiao.action.b" />
        <category android:name="com.abiao.category.a" />
        <category android:name="com.abiao.category.b" />
        <data android:mimeType="text/plain"/>
    </intent-filter>

    <intent-filter>
        <action android:name="com.abiao.action2.a" />
        <action android:name="com.abiao.action2.b" />
        <category android:name="com.abiao.category2.a" />
        <category android:name="com.abiao.category2.b" />
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

一个Activity可以有一个或多个intent-filter,一个intent-filter可以有多个action、category和data,只要一个Intent同时匹配到一个intent-filter中的action、category和data就表示匹配成功,否则失败。

action 匹配规则

  1. action是一个字符串,系统有一些预定义的action,我们也可以在应用中定义自己的action。
  2. 匹配规则是字符串完全相同,区分大小写。
  3. Intent中只存在一个action。
  4. Intent中的action需要存在且和规则中的其中一个action相同,否则匹配失败。

category 匹配规则

  1. category是一个字符串,系统有一些预定义的category,我们也可以在应用中定义自己的category。
  2. 匹配规则是字符串完全相同,区分大小写。
  3. Intent中可以存在多个category。
  4. 匹配规则是Intent中任意一个category都能和category过滤规则中的其中一个匹配上。
  5. 如果Intent中没有category,依然能匹配成功,因为系统在调用startActivity或者startActivityForResulet的时候默认添加了android.intent.category.DEFAULT。
  6. 在匹配规则中我们需要添加android.intent.category.DEFAULT这个category

data 匹配规则

data的结构如下:

xml 复制代码
<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:mimeType="string"/>

data主要是由URI和mimeType组成

  1. mimeType指媒体类型(image/jepg、audio/mpeg4-generic和video/* 等表示图片、文本、视频),有默认值(content和File)

  2. URI 由以下结构

    xml 复制代码
       <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
    • scheme URI 模式,如http、file、content等,如果不指定则URI无效。
    • host URI 主机名,如www.baidu.com,如果不指定则无效
    • port URI 端口号,如 80
    • path 完整的路径信息
    • pathPattern 完整的路径信息,包含通配符*,*表示0个或者多个任意字符
    • pathPrefix 路径前缀信息
  3. 设置方法如下

kotlin 复制代码
intent.setDataAndType(Uri.parse(""),"image/png")  
//会将mimeType设置为null等价于intent.setDataAndType(Uri.parse(""),null)  
intent.setData(Uri.parse("file://abs"))  
//会将uri设置为null等价于intent.setDataAndType(null,"image/png")  
intent.setType("image/png")
  1. 匹配规则如下
  • data存在且只要匹配上规则上其中一条,表示匹配成功。
  1. 特殊写法,以下两种写法作用是一样的:
xml 复制代码
   <!--写法一-->
   <intent-filter>
        <data android:mimeType="text/plain" android:scheme="file" android:host="www.baidu.com"/>
    </intent-filter>
 <!--写法二-->
   <intent-filter>
        <data android:mimeType="text/plain"/>
        <data android:scheme="file"/>
        <data android:host="www.baidu.com"/>
    </intent-filter>
  1. 查找失败判断 通过隐式Intent启动Activity的时候可能会遇到找不到Activity的情况,这个时候系统会报错导致App崩溃,为了避免这种问题有以下两种方法可以判断是否存在Activty,如果这两个方法返回值不等于null,就可以启动成功。
    • PackageManager的resolveActivity(Intent,flags)方法,flags 需要MATCH_DEFAULT_ONLY这个标记位
    • Intent的resolveActivity

生命周期

正常情况下的生命周期

正常情况下,Activity会经历以下的生命周期

  1. onCreate

    表示Activity正在被创建,这里可以做一些初始化工作(加载界面布局资源,初始化Activity所需要的一些数据)

  2. onRestart

    表示Activity正在重新启动,当当前Activity从不可见变为可见状态时就会被调用

  3. onStart

    表示Activity正在被启动,这时候Activity已经可见但是还未出现在前台,无法与用户交互。

  4. onResume

    Activity已经可见,在前台出现并开始活动,可以与用户交互。

  5. onPause

    表示Activity正在停止,在正常情况下马上会调用onStop,但是在特殊情况下快速返回当前Activity,onReseume就会被调用,但这种情况很极端,用户很难遇见。这里可以做一些存储数据、停止动画之类的操作。但是不能太耗时,因为只有onPause执行完了新的Activity的onResume才会执行。

  6. onStop

    Activity即将停止,同样不能做太耗时工作,可以比onPause稍微重一点的任务

  7. onDestroy

    表示Activity即将销毁,这里可以做一些回收工作和最终的资源释放。

  • 注意
  1. 当用户打开了一个新的Activity,当前Activity回调如下:onPause -> onStop,但是当新的Activit是一个透明Activity时,当前Activity不会回调onStop.
  2. 当用户按下back键时,回调如下:onPause -> onStop -> onDestroy
  3. onCreate和onDestroy配对的,表示Activity的创建与销毁,整个生命周期中只调用一次。onStart和onStop也是配对的,便是Activity的可见与否,随着用户的操作或者屏幕点亮与否可能会回调多次。onResume和onPause也是配对的,表示Activity是否在前台。与onStart和onStop一样可能会被调用多次。

异常情况下的生命周期

Activity的生命周期除了受到用户操作影响之外也很可能因为资源相关的系统配置改变或者内存不足的时候被杀死。

资源相关的配置改变导致Activity被杀死并重建

资源相关配置会改变与系统的资源加载机制有关,举个例子,当我们把一张图片放在drawable目录后,可以通过Resource去获取,为了兼容不同设备我们可能会在其他目录(drawable-mdpi、drawable-hdpi、drawable-land等等),系统会根据不同的机型加载不同的Resource资源。但是假如Activity当前处于竖屏状态突然旋转屏幕,系统配置就发生了改变。默认情况下系统就会销毁当前Activity并重新创建。

这个时候当前Activity的生命周期如下: 系统资源被改变之后,Activity的onPause、onStop、onDestory会被调用,因为是异常终止所以,在onStop被调用之前系统会调用onSaveInstanceState来保存当前Activity的状态。Activity被重新创建之后,系统会把Activity销毁时在onSaveInstanceState中保存的Bundle对象作为参数传个onRestoreInstanceState和onCreate,因此我们可以通过onRestoreInstanceState和onCreate来判断Activity是否被重新创建了。onRestoreInstanceState的调用时机是在onStart之后。

资源改变时候我们可以通过配置configChanges这个属性来让Activity不重新创建

xml 复制代码
android:configChanges="orientation"

内存不足倒是低优先级的Activity被杀死

Activity的优先级

  • 前台Activty 正在和用户交互的Activity 优先级最高
  • 可见但非前台Activity
  • 后台Activity 已经被暂停了的Activity

当系统内存不足的时候,系统会按照以上优先级杀死目标Activity,并通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。

Activity启动模式

STANDARD 标准模式(默认)

每次启动都会重新创建一个新的Activity实例,不管当前实例是否存在。一个任务栈中可以存在多个实例,每个实例也可以属于不同的任务栈。谁启动了当前Activity那么这个Activity就在谁所在的那个任务栈当中。注意,如果是非Activity(如ApplicationContext)类型的Context去启动了一个新Activity会报错,是因为ApplicationContext没有所谓的任务栈,要启动需要使用下面准备介绍的SINGLE_TASK启动模式

SINGLE_TOP 栈顶复用模式

如果要创建的Activity位于栈顶,那么将不会重新创建实例,同时会调用Activity的onNewIntent方法,当前Activity的onCreate、onStart方法不会被系统调用。假如创建的Activity不在栈顶,那么就会重新创建一个新的实例。

SINGLE_TASK 站内复用

一种单实例模式,只要Activity在任务栈中存在,多次去启动改Activity都不会重新创建实例,同时和SINGLE_TOP一样会调用它的onNewIntent方法。当用该模式启动之后,系统会寻找是否存在其想要的任务栈,如果不存在则创建新的任务栈正常创建其实例,如果存在则将其实例调到栈顶并复用(调用其onNewIntent方法)。

SINGLE_INSTANCE 单实例模式

加强版的SINGLE_TASK,具有SINGLE_TASK的所有特性,另外具有此模式的Activity只能独立位于单独的任务栈中。由于SINGLE_TASK的特性,通过其创建的Activity都位于这个任务栈内,且只能存在一个实例。

相关推荐
2501_916008895 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
我科绝伦(Huanhuan Zhou)5 小时前
MySQL一键升级脚本(5.7-8.0)
android·mysql·adb
怪兽20147 小时前
Android View, SurfaceView, GLSurfaceView 的区别
android·面试
龚礼鹏7 小时前
android 图像显示框架二——流程分析
android
消失的旧时光-19437 小时前
kmp需要技能
android·设计模式·kotlin
帅得不敢出门8 小时前
Linux服务器编译android报no space left on device导致失败的定位解决
android·linux·服务器
雨白9 小时前
协程间的通信管道 —— Kotlin Channel 详解
android·kotlin
TimeFine11 小时前
kotlin协程 容易被忽视的CompletableDeferred
android
czhc114007566312 小时前
Linux1023 mysql 修改密码等
android·mysql·adb
GOATLong13 小时前
MySQL内置函数
android·数据库·c++·vscode·mysql