一起来聊聊Android基础之Activity

一起来聊聊Android基础之Activity

标签(空格分隔): Android面试知识 Android开发


什么是Activity?

Activity是Android提供的一个可以和用户进行交互。

Activity的状态

  1. running:此Activity位于屏幕前台并具有焦点,可以直接和用户交互。
  2. paused:此Activity未获得焦点,但对用户仍然可见。比如该Activity上层有一个透明的Activity,或者被Dialog覆盖。
  3. stopped:此Activity未获得焦点,对用户不可见。比如该Activity上层有一个非透明,非Dialog样式的Activity覆盖。注意和paused的区别:是否对用户可见

Activity的生命周期

Activity的生命周期可以参考Google提供的示例代码,注释中讲的很清楚了。

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}

再上一张Google的“Activity生命周期图”,这张图把Activity的生命周期表达的非常详细了,多看几遍会有新的理解。
此处输入图片的描述

再从几种常见场景进行分析:
此处输入图片的描述

保存Activity状态

保存Activity状态信息的两个关键API:

protected void onSaveInstanceState(Bundle outState)
protected void onRestoreInstanceState(Bundle savedInstanceState)

此处输入图片的描述

当我们需要在Activity被销毁的时候保存某些数据时,可以在onRestoreInstanceState将待保存的数据写到savedInstanceState中,从而在Activity重建时可以在onSaveInstanceState回调方法的outState中读出数据。
onSaveInstanceState方法一般在onPause和onStop方法之间被回调,对应的,onRestoreInstanceState方法一般在onStart和onResume方法之间被回调。

注:无法保证系统会在销毁您的 Activity 前调用 onSaveInstanceState(),因为存在不需要保存状态的情况(例如用户使用“返回”按钮离开您的 Activity 时,因为用户的行为是在显式关闭 Activity)。 如果系统调用 onSaveInstanceState(),它会在调用 onStop() 之前,并且可能会在调用 onPause() 之前进行调用。
因此您只应利用它来记录 Activity 的瞬态(UI 的状态)— 切勿使用它来存储持久性数据,而应使用 onPause() 在用户离开 Activity 后存储持久性数据(例如应保存到数据库的数据)。

您只需旋转设备,让屏幕方向发生变化,就能有效地测试您的应用的状态恢复能力。 当屏幕方向变化时,系统会销毁并重建 Activity,以便应用可供新屏幕配置使用的备用资源。

默认实现会为布局中的每个 View 调用相应的 onSaveInstanceState() 方法,以便在重建 Activity 时自动保存和恢复对 UI 所做的任何可见更改。例如,EditText 小部件保存用户输入的任何文本,CheckBox 小部件保存复选框的选中或未选中状态。您只需为想要保存其状态的每个小部件提供一个唯一的 ID(通过 android:id 属性)。如果小部件没有 ID,则系统无法保存其状态。

Android的进程优先级

android将进程的优先级分为5个层次,按照优先级由高到低排列如下:
1.前台进程(Foreground process)。正在和用户进行交互的进程,android系统依据下面的条件来将一个进程标记为前台进程:

  • 该进程持有一个正在与用户交互的Activity(也就是这个activity的生命周期方法走到了onResume()方法)。
  • 该进程持有一个Service,并且这个Service与一个正在和用户交互的Activity进行绑定。
  • 该进程持有一个前台运行模式的Service(也就是这个Service调用了startForegroud()方法)。
  • 该进程持有一个正在执行生命周期方法(onCreate()、onStart()、onDestroy()等)的Service。
  • 该进程持有一个正在执行onReceive()方法的BroadcastReceiver。

2.可见进程(Visible process)。它表明虽然该进程没有持有任何前台组件,但是它还是能够影响到用户看得到的界面。android系统依据下面的条件将一个进程标记为可见进程:

  • 该进程持有一个非前台Activity,但这个Activity依然能被用户看到(也就是这个Activity调用了onPause()方法)。例如,当一个activity启动了一个对话框,这个activity就被对话框挡在后面。
  • 该进程持有一个与可见Activity绑定的Service。

3.服务进程(Service process)。除了符合前台进程和可见进程条件的Service,其它的Service都会被归类为服务进程。

4.后台进程(Background process)。持有不可见Activity(调用了onStop()方法)的进程即为后台进程。通常情况下都会有很多后台进程,当内存不足的时候,在所有的后台进程里面,会按照LRU(最近使用)规则,优先回收最长时间没有使用过的进程。

5.空进程(Empty process)。不持有任何活动组件的进程。保持这种进程只有一个目的,就是为了缓存,以便下一次启动该进程中的组件时能够更快响应。

Android的任务和返回栈

任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。

Android中设计的任务,返回栈和启动模式是为了更好的管理Activity。
通过使用 <activity> 清单文件元素中的属性和传递给 startActivity() 的 Intent 中的标志,您可以执行所有这些操作以及其他操作。

由于后面讲解属性和几种启动模式的部分都会涉及到affinity(亲和力),所以先简单提一下affinity这个概念。

摘录来源:基础总结篇之三:Activity的task相关

affinity对于Activity来说就好像它的身份证一样,可以告诉所在的task,自己属于这个task中的一员;拥有相同affinity的多个Activity理论同属于一个task,task自身的affinity决定于根Activity的affinity值。affinity在什么场合应用呢?1.根据affinity重新为Activity选择宿主task(与allowTaskReparenting属性配合工作);2.启动一个Activity过程中Intent使用了FLAG_ACTIVITY_NEW_TASK标记,根据affinity查找或创建一个新的具有对应affinity的task。

默认情况下,一个应用内的所有Activity都具有相同的affinity,都是从Application(参考的taskAffinity属性)继承而来,而Application默认的affinity是中的包名,我们可以为设置taskAffinity属性值,这样可以应用到下的所有,也可以单独为某个Activity设置taskAffinity。

task属性

<activity>中定义了几个常见的task相关属性,它们分别代表了task内部不同的行为特征。

  1. android:allowTaskReparenting
    当启动 Activity的任务接下来转至前台时,Activity是否能从该任务转移至与其有亲和关系的任务—“true”表示它可以转移,“false”表示它仍须留在启动它的任务处。 默认值为“false”。

    说直白一点,从allowTaskReparenting的名字来解读,就是允许Task重新找爸爸。
    例如在一个应用A中要查看一个web页面,在启动系统浏览器Activity后,这个Activity实例和当前应用A处于同一个task,当我们的应用A退居后台之后用户再次从主界面启动应用A,此时这个Activity实例将会重新宿主到Browser应用的task内,在我们的应用A中将不会再看到这个Activity实例,而如果此时启动Browser应用,就会发现,第一个界面就是我们刚才打开的web页面,证明了这个Activity实例确实是宿主到了Browser应用的task内。

  2. android:alwaysRetainTaskState
    系统是否始终保持 Activity 所在任务的状态 —“true”表示保持,“false”表示允许系统在特定情况下将任务重置到其初始状态。 默认值为“false”。该属性只对任务的根 Activity 有意义;对于所有其他 Activity,均忽略该属性。
    正常情况下,当用户从主屏幕重新选择某个任务时,系统会在特定情况下清除该任务(从根 Activity 之上的堆栈中移除所有 Activity)。 系统通常会在用户一段时间(如 30 分钟)内未访问任务时执行此操作。
    不过,如果该属性的值是“true”,则无论用户如何到达任务,将始终返回到最后状态的任务。 例如,在网络浏览器这类存在大量用户不愿失去的状态(如多个打开的标签)的应用中,该属性会很有用。

  3. android:clearTaskOnLaunch
    如果在任务的根 Activity 中将此属性设置为 “true”,则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。 换而言之,它与 alwaysRetainTaskState 正好相反。 即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。

  4. android:finishOnTaskLaunch
    这个属性和android:allowReparenting属性相似,不同之处在于allowReparenting属性是重新宿主到有共同affinity的task中,而finishOnTaskLaunch属性是销毁实例。如果这个属性和android:allowReparenting都设定为“true”,则以这个属性为准。

Intent 标志

启动 Activity 时,您可以通过在传递给 startActivity() 的 Intent 中加入相应的标志,修改 Activity的默认行为。

  • FLAG_ACTIVITY_NEW_TASK
    在新任务中启动 Activity。如果已为正在启动的 Activity 运行任务,则该任务会转到前台并恢复其最后状态,同时 Activity 会在 onNewIntent() 中收到新 Intent。
    这会产生与 “singleTask”launchMode 值相同的行为。

  • FLAG_ACTIVITY_SINGLE_TOP
    如果正在启动的 Activity 是当前 Activity(位于返回栈的顶部),则现有实例会接收对 onNewIntent() 的调用,而不是创建 Activity 的新实例。
    这会产生与 “singleTop”launchMode 值相同的行为。

  • FLAG_ACTIVITY_CLEAR_TOP
    如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。
    产生这种行为的 launchMode 属性没有值。

另外还有几种不常见的Intent标志,在此就不介绍了。

Activity的启动模式

启动模式允许您定义 Activity 的新实例如何与当前任务关联。 您可以通过两种方法定义不同的启动模式:

  1. 使用清单文件
    在清单文件中声明 Activity 时,您可以指定 Activity 在启动时应该如何与任务关联。
  2. 使用 Intent 标志
    调用 startActivity() 时,可以在 Intent 中通过setFlags方法加入一个标志,用于声明新 Activity 如何(或是否)与当前任务关联。
清单文件Intent标志说明
standard默认默认模式,Activity会多次实例化
singleTopFLAG_ACTIVITY_SINGLE_TOP栈顶复用
singleTaskFLAG_ACTIVITY_NEW_TASK栈内复用
singleInstance与 “singleTask” 类似,但会创建一个新的栈存放该Activity的实例,并且这个栈中有且仅有这一个实例。

“singleTask”细节讨论

“singleTask”启动模式由于其特殊性,经常使人迷惑。所以,重点讨论一下设置了”singleTask”启动模式的Activity的特点:

可以从三个方面来理解singTask:

  1. 系统创建新任务并实例化位于新任务底部的 Activity。
    系统是否会为Activity创建新任务还跟taskAffinity属性有关。设置了”singleTask”启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity相同的任务(默认同一个应用的affinity相同,都为包名);
    如果存在这样的任务,它就会在这个任务中启动,否则就会在新任务中启动。因此,如果我们想要设置了”singleTask”启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。

  2. 如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent()方法向其传送Intent,而不是创建新实例。
    如果设置了”singleTask”启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例,如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity实例会位于任务的堆栈顶端中。

  3. 一次只能存在 Activity 的一个实例。

多数人认为通过清单文件为Activity指定singTask启动模式(方式一)和在Intent设置FLAG_ACTIVITY_NEW_TASK标志位(方式二)是一样的,其实二者还是有区别的:

  • 第二种方式的优先级要高于第一种,当两种同时存在时,以第二种为准。
  • 针对“一次只能存在Activity的一个实例”这条特性,第二种方式只有在为Activity设置一个独立的taskAffinity属性值的情况下才生效。

    举两个例子说明:
    例一:
    app中有两个Activity,A和B,其中A启动B,然后B通过方式二启动自身两次。默认情况下返回栈的顺序是:A-B-B’-B”。可通过adb shell dumpsys activity查看返回栈中Activity信息。

    • 当通过方式一指定B为singTask启动模式时,返回栈的顺序是:A-B,并且两个Activity实例都在同一个TaskRecord中;
    • 当通过方式一指定B为singTask启动模式时,并为B设置一个独立的taskAffinity属性值时,返回栈的顺序是:A-B,并且两个Activity实例在不同TaskRecord中;
    • 通过方式二在Intent设置FLAG_ACTIVITY_NEW_TASK标志位时,返回栈的顺序是:A-B-B'-B''。说明FLAG_ACTIVITY_NEW_TASK标志位没有起作用。并且多个Activity实例都在同一个TaskRecord中
    • 通过方式二在Intent设置FLAG_ACTIVITY_NEW_TASK标志位,并为B设置一个独立的taskAffinity属性值时,返回栈的顺序是:A-B并且两个Activity实例在不同TaskRecord中;
    Intent intent = new Intent(MainActivity.this, MainActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }

    例二:
    app1和app2分别有两个Activity,1A,1B和2A,2B。
    启动顺序为:1A-1B-2B-1B-2B

    • 默认情况:1A-1B-2B-1B’-2B’都在同一个Task中。
      此处输入图片的描述
    • 当通过方式一指定1B和2B为singTask启动模式时,并为1B和2B设置一个独立的taskAffinity属性值时,返回栈最后的顺序是:1A-1B-2B;
      此处输入图片的描述
    • 通过方式二在启动1B和2B的Intent设置FLAG_ACTIVITY_NEW_TASK标志位时,返回栈最后的顺序是:1A-1B-1B’-2B-2B’;
      此处输入图片的描述
    • 通过方式二在启动1B和2B的Intent设置FLAG_ACTIVITY_NEW_TASK标志位,并为1B和2B设置一个独立的taskAffinity属性值时,返回栈最后的顺序是:1A-1B-2B;
      此处输入图片的描述

总结一下Activity的singleTask模式,主要有两点:
1. 在新任务中新建实例。
默认不会在新任务中新建Activity实例,除非设置了Activity的affinity。
2. 只能存在 Activity 的一个实例。
在清单文件中指定launchMode为singleTask,则不会产生新的该Activity实例。
指定Intent的flag为FLAG_ACTIVITY_NEW_TASK,会产生新的该Activity实例,除非再为该Activity单独指定affinity。

Intent和Intent过滤器

鉴于这篇文章太长了,这部分内容就单独拿出来聊吧。Intent和Intent过滤器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值