本文最后更新于:1 个月前

Activity 类是 Android 应用的关键组件,提供窗口供应用在其中绘制界面。

了解 Activity 生命周期

Activity 生命周期的简化图示

onCreate()

系统首次创建 Activity 时触发,在此方法中,需执行基本应用启动逻辑(声明界面、定义成员变量、配置某些界面),该逻辑在 Activity 的整个生命周期中只应发生一次。可以在此方法中将数据绑定到列表、将 Activity 与 ViewModel 相关联、实例化某些类范围变量。

onStart()

调用此方法使 Activity 对用户可见,为 Activity 进入前台并支持交互做准备。例如,应用此方法来初始化维护界面的代码。Activity不会一直处于“已开始”状态,一旦此回调结束,Activity便会进入“已恢复”状态,系统将调用onResume()方法。

onResume()

应用与用户交互的状态,用户会一直保持这种状态,直到某些事件发生,让焦点远离应用。当发生中断事件时,Activity进入“已暂停”状态,系统调用 onPause() 回调。如果Activity从“已暂停”状态返回“已恢复”状态,系统将再次调用 onResume() 方法。

onPause()

系统将此方法视为用户正在离开 Activity 的第一个标志(尽管这并不总是意味着活动正在遭到销毁);此方法表示 Activity 不再位于前台(尽管如果用户处于多窗口模式,Activity 仍然可见)。使用 onPause() 方法暂时或调整当 Activity 处于“已暂停”状态时不应继续(或应有节制地继续)的操作,以及希望很快恢复的操作。Activity 进入此状态有多个原因,例如:

  • 如 onResume() 部分所述,某个事件会中断应用执行。这是最常见的情况。
  • 在 Android7.0(API级别24)或更高版本中,有多个应用在多窗口模式下运行。无论何时,都只有一个应用(窗口)可以拥有焦点,因此系统会暂停所有其它应用。
  • 有新的半透明 Activity(例如对话框)处于开启状态。只要 Activity 仍然部分可见但并未处于焦点之中,它便会一直暂停

还可以在此方法中释放系统资源、传感器(如GPS)手柄,或当 Activity 暂停且用户不需要它们时仍然可能影响电池续航时间的任何资源。
如果处于多窗口模式,则“已暂停”的 Activity 仍完全可见。因此,应考虑使用 onStop() 而非 onPause() 来完全释放或调整与界面相关的资源和操作,以便更好地支持多窗模式。
onPause() 执行非常简单,而且不一定要有足够的时间来执行保存操作。因此,不应该使用 onPause() 来保存应用或用户数据、进行网络调用或执行数据库事务。因为在该方法完成之前,此类工作可能无法完成。相反,应在 onStop() 期间执行高负载的关闭操作。

onStop()

如果Activity对用户不可见,则说明其进入“已停止状态”,此时系统将会调用onStop()回调。在 onStop() 方法中,应用应释放或调整应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从细粒度位置更新切换到粗粒度位置更新。
当 Activity 进入“已停止”状态时,Activity 对象会继续驻留在内存中:该对象将维护所有状态和成员信息,但不会附加到窗口管理器。状态恢复后,Activity 会重新调用这些信息。无需重新初始化在回调方法导致 Activity 进入“已恢复”状态期间创建的组件。系统还会追踪布局中每个View对象的当前状态,如果用户在 EditText 微件中输入文本,系统将保留文本内容,因此无需保存和恢复文本。

onDestory()

销毁 Ativity 之前,系统会先调用 onDestroy()。系统调用此回调的原因如下:

  • Activity 正在结束(由于用户彻底关闭 Activity 或由于系统为 Activity 调用 finish())。
  • 由于配置变更(例如设备旋转或多窗口模式),系统暂时销毁 Activity。

可以使用 isFinishing() 方法区分这两种情况。
应使用 ViewModel 对象来容纳 Activity 的相关视图数据,如果由于配置变更而重新创建 Activity,则 ViewMode 不必执行任何操作,因为系统将保留 ViewModel 并将其提供给下一个 Activity 实例。如果不重新创建 Activity,ViewModel 将调用 onCleared() 方法,以便在 Activity 遭到销毁前清除所需的任何数据。onDestory() 回调前应释放先前的回调(如onStop())尚未释放的所有资源。

Activity 状态和从内存中弹出

系统终止进程的可能性 进程状态 Activity状态
小 前台(拥有或即将获得焦点) 已创建 已开始 已恢复
中 后台(失去焦点) 已暂停
大 后台(不可见) 已停止
空 已销毁
表1.进程生命周期和Activity状态之间的关系

使用 onSaveInstanceState() 保存简单轻量的界面状态

当 Activity 开始停止时,系统会调用 onSaveInstanceState() 方法,以便 Activity 可以将状态信息保存到实例状态 Bundle 中。此方法的默认实现保存有关 Activity 视图层次结构状态的瞬态信息,例如 EditText 微件中的文本或 ListView 微件的滚动位置。
如要保存持久化数据(例如用户首选项或数据库中的数据),应在 Activity 位于前台时抓住合适机会。如果没有这样的时机,应在执行 onStop() 方法期间保存此类数据。

使用保存的实例状态恢复 Activity 界面状态

  • 无论系统是新建 Activity 实例还是重新创建之前的实例,都会调用 onCreate() 方法,所以在尝试读取之前,必须检查状态 Bundle 是否为 null。如果为 null,系统将新建 Activity 实例,而不会恢复之前销毁的实例。
  • 选择实现系统在 onStart() 方法之后调用的 onRestoreInstanceState(),而不是在 onCreate() 期间恢复状态。仅当存在要恢复的已保存状态时,系统才会调用 onRestoreInstanceState(),因此无需检查 Bundle 是否为 null。

ActivityA 启动 ActivityB 时

  1. ActivityA 的 onPause() 方法执行。
  2. ActivityB 的 onCreate()、onStart() 和 onResume() 方法依次执行。(ActivityB 现在具有用户焦点。)
  3. 然后,如果 ActivityA 在屏幕上不再可见,则其 onStop() 方法执行。

启动模式

使用清单文件设置(launchMode 属性)

  • "standard" (默认模式)

    默认值。系统在启动该 Activity 的任务中创建 Activty 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。

  • "singleTop"
    如果当前任务的顶部已存在 Activity 的实例,则系统会通过其 onNewIntent() 方法来将 intent 传递给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。

  • "singleTask"
    系统会创建新任务,并实例化新任务的根 Activity。但是,如果另外的任务中已存在该 Activity 的实例,则系统会通过调用其 onNewIntent() 方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在。

  • "singleInstance"
    与 "singleTask" 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

使用Intent标记

  • FLAG_ACTIVITY_NEW_TASK
    在新任务中启动 Activity。如果现在启动的 Activity 已经有任务在运行,则系统会将该任务转到前台并恢复其最后的状态,而 Activity 将在 onNewIntent() 中收到新的 intent。此与 "singleTask" 产生的行为相同。

  • FLAG_ACTIVITY_SINGLE_TOP
    如果要启动的 Activity 是当前 Activity(即位于返回堆栈顶部的 Activity),则现有实例会收到对 onNewIntent() 的调用,而不会创建 Activity 的新实例。此与 "singleTop" 产生的行为相同。

  • FLAG_ACTIVITY_CLEAR_TOP
    如果要启动的 Activity 已经在当前任务中运行,则不会启动该 Activity 的新实例,而是会销毁位于它之上的所有其他 Activity,并通过 onNewIntent() 将此 intent 传送给它的已恢复实例(现在位于堆栈顶部)。

FLAG_ACTIVITY_CLEAR_TOP 最常与 FLAG_ACTIVITY_NEW_TASK 结合使用。将这两个标记结合使用,可以查找其他任务中的现有 Activity,并将其置于能够相应 intent 的位置。

处理亲和性

“亲和性”表示 Activity 倾向于属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此具有亲和性。因此,在默认情况下,同一应用中的所有 Activity 都倾向于位于同一任务。不过,您可以修改 Activity 的默认亲和性。在不同应用中定义的 Activity 可以具有相同的亲和性,或者在同一应用中定义的 Activity 也可以被指定不同的任务亲和性。

清除返回堆栈

如果用户离开任务较长时间,系统会清除任务中除根 Activity 以外的所有 Activity。当用户再次返回到该任务时,只有根 Activity 会恢复。系统之所以采取这种行为方式是因为,经过一段时间后,用户可能已经放弃了之前执行的操作,现在返回任务是为了开始某项新的操作。

在进程之间发送数据

Binder 事务缓冲区的大小固定有限,目前为 1MB,由进程中正在处理的所有事务共享。由于此限制是进程级别而不是 Activity 级别的限制,因此这些事务包括应用中的所有 binder 事务,例如 onSaveInstanceState,startActivity 以及与系统的任何互动。超过大小限制时,将引发 TransactionTooLargeException。

对于 savedInstanceState 的具体情况,应将数据量保持在较小的规模,因为只要用户可以返回到该 Activity,系统进程就需要保留所提供的数据(即使 Activity 的进程已终止)。我们建议您将保存的状态保持在 50k 数据以下。

Fragment

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

add()

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

replace()

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Intent 上一篇
Resource 下一篇