本文最后更新于: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 下一篇