Activities

本文深入讲解Android中Activity的生命周期管理,包括不同阶段的回调方法、配置变化时的状态保存与恢复、多窗口模式下的生命周期变化等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Activities

https://developer.android.com/guide/components/activities/index.html

一、Introduction to Activities

  Activity是Android应用的核心组件,通过不同阶段的生命周期回调方法控制组件。

  1. onCreate: 初始化必须的页面组件,创建视图/绑定数据,调用setContentView;
  2. onStart: 这个阶段Activity对用户可见,为进入前台和用户交互做最后的准备;
  3. onResume: 这个阶段Activity已经处于task顶部,接收用户的交互输入;
  4. onPause: Activity失去焦点时调用,界面处于部分可见的状态,不要在这个阶段保存数据、请求网络或者执行数据库访问的动作;
  5. onStop: 这个阶段界面对用户完全不可见,要么销毁,要么即将被新页面覆盖;
  6. onRestart: 从onStop状态重新恢复可见,下一个生命周期是onStart->onResume;
  7. onDestroy: 这个阶段Activity即将被销毁,在回调中要释放Activity持有的资源;

二、The Activity Lifecycle

1. Activity-lifecycle concepts

  1. onCreate: 如果Activity没有创建过,入参Bundle为null,如果因为屏幕旋转导致Activity重建,这里的Bundle对象就是销毁时onSaveInstanceState方法的入参对象(hashCode相等),之后也是onRestoreInstanceState的入参对象,所以重建activity时,onRestoreInstanceState在onStart和onResume之间被调用。
屏幕旋转时调用的Activity的生命周期事件:
I/MainActivity: onPause
I/MainActivity: onSaveInstanceState=253635896
I/MainActivity: onStop
I/MainActivity: onDestroy
I/MainActivity: onCreate=253635896
I/MainActivity: onStart
I/MainActivity: onRestoreInstanceState=253635896
I/MainActivity: onResume
  1. onStart: 进入这个阶段时,页面对用户可见,通常这个阶段的时间很短,可以用于注册BroadcastReceiver

  2. onResume: 重新初始化在onPause里被释放的组件或资源,重新启动动画(仅在页面处于焦点状态时可用)

  3. onPause: 在这个阶段停止动画,和音乐播放。有几种情况会使Activity进入这个状态:用户导航切换、前后台切换、7.0的多窗口模式、打开如dialog的半透明窗口。在onPause里可以释放一些资源,比如:BroadcastReceiver、相机、传感器处理以及其他不需要的资源。不要在这个阶段执行费时的操作,重的操作应该放到onStop里。

  4. onStop: 进入这个阶段的Activity已经对用户不可见了,这个阶段适合做CPU密集的关闭任务,比如写数据库。

  5. onDestroy: 当调用finish()方法(用户主动导航触发)或者系统为了节省空间而销毁进程时,Activity会进入这个状态,通过isFinishing()方法可以判别这两种情况。

2. Activity state and ejection from memory

  系统不会直接杀死Activity本身,而是杀死Activity所在进程,同时回收其他的所有资源。进程是否会被杀死决定于进程的状态,而进程的状态又是由Activity的状态所决定,Activity状态/进程状态/被杀死的可能性三者的对应关系如下:

Likelihood of being killedProcess stateActivity state
LeastForeground(having or about to get focus)Created
Started
Resumed
MoreBackground(lost focus)Paused
MostBackground(not visible)Stopped
MostEmptyDestroyed

3. Navigating between activities

Starting one activity from another

  对于打开新Activity的需求,不需要返回参数的直接使用startActivity(Intent),需要返回参数的使用startActivityForResult(Intent intent, int requestCode)这个API,返回参数通过onActivityResult(int requestCode, int resultCode, Intent data)方法返回,requestCode用于标识具体的请求,resultCode用于标识请求处理的结果码,如果请求处理失败或者子页面发生了crash,resultCode返回 RESULT_CANCELED==0,请求处理成功返回 RESULT_OK==-1 或者用户自定义的码 >=RESULT_FIRST_USER==1

Coordinating activities

  当启动或销毁Activity的时候,同时有两个Activity需要经历生命周期的变化:

  1. A启动B:A.onPause()->B.onCreate()->B.onStart()->B.onResume()->A.onSaveInstanceState()->A.onStop();
  2. B销毁:B.onPause()->A.onRestart()->A.onStart()->A.onResume()->B.onStop()->B.onDestroy();
  3. 旋转B:onPause()->onSaveInstanceState()->onStop()->onDestroy()->onCreate()->onStart()->onRestoreInstanceState()->onResume();

      从上面的规则可以看到,系统总是保证用户期望的Activity尽快出现,所以对当前的Activity执行完onPause()生命周期后,就立即执行目标Activity的生命周期,等目标Activity的生命周期完成到onResume()之后,再继续处理前一个Activity的回收方法onStop()/onDestroy()。

Saving and restoring activity state

  如果用户通过返回键关闭当前的Activity,系统将这种操作定义为销毁当前的Activity,如果因为系统资源的限制导致Activity被销毁,系统将会记住这些Activity的状态,以K-V对的形式保存在Bundle中,待用户重新导航回来时,再重置这些状态。系统默认使用Bundle对象保存保存视图对象的信息,如EditView的文本内容。

Save your activity state

  当系统调用onSaveInstanceState()方法时,应用可以通过重写这个方法来添加自己的K-V状态信息,默认情况下系统会保存视图树的临时状态信息,比如EditView中的文本、ListView滚动的位置等。onSaveInstanceState()方法的生命周期在onPause()和onStop()的中间被调用。

  为了保证系统能够正确地把数据恢复到View上,系统在执行onSaveInstanceState()的时候还会记录由android:id属性定义的唯一ViewID。

Restore your activity state

  onCreate()和onRestoreInstanceState()方法在重新创建Activity时都会收到同一个入参Bundle对象,不同的是onCreate()在首次创建Activity的时候也会被调用,而第一次创建的时候入参Bundle=null,所以在onCreate中需要检查入参是否为null来判断本次是新创建还是restore重建的操作。重写onSaveInstanceState()/onRestoreInstanceState()方法时记得调用super()默认实现。

三、Activity State Changes

  不同的事件(由用户触发或者系统触发)会引起Activity状态的改变。

Configuration change occurs

  有很多事件可以触发配置的变化,最常见的是屏幕旋转引起的portrait和landscape方法方向的变化,其他的触发条件比如语言或者输入设备的变化。

  当配置变化时,Activity需要被销毁并重新创建,如果需要保存Activity的状态数据,需要重写onSaveInstanceState()方法,并在onCreate()或者onRestoreInstanceState()方法中恢复数据。

Handling multi-window cases

  当应用进入7.0系统(API24)的多窗口模式时,系统会通知当前Activity配置发生了变化,然后运行对应生命周期的方法。在多窗口模式下,只有一个应用能够获取焦点并处于前台运行状态,其他的应用都处于Paused状态。当用户从应用A切换到应用B时,A.onPause()方法和B.onResume()方法会被调用。

Activity or dialog appears in foreground

  遮挡与半遮挡的生命周期回调区别:

  1. A启动B半遮挡:A.onPause()
  2. A从半遮挡恢复:A.onResume()
  3. A启动B全覆盖:A.onPause()->A.onStop()
  4. A从全覆盖恢复:A.onRestart()->A.onStart()->A.onResume()
  5. A从全覆盖到重新创建:A.start()->onResume(),不会调用onRestart()

User taps Back button

  点击回退按键时,系统认为用户期望关闭当前页面,并且不会再回来,所以会调用onPause()->onStop()->onDestroy()生命周期回调来销毁当前Activity,在这种情况下,系统不会调用onSaveInstanceState()方法。应用可以重写onBackPressed()方法实现自定义的行为,比如确认退出。

四、Tasks and Back Stack

  Task是Activity的集合,用于执行一类Job,Activity以栈(back stack)的形式保存在Task中。当用户点击桌面的应用图标时,如果应用程序的task已经存在,就会切换到前台,否则系统会创建一个新的task并启动MAIN Activity作为应用的入口。

  当前Activity启动的新Activity会被push到栈顶并获取焦点,旧的Activity保留在栈内,处于stopped状态。当用户返回到前一个页面时,栈顶的Activity被pop出来并销毁,再重新激活前一个Activity。有一个基本的原则是,栈里的Activity永远都不会被重排序,只能进行last in, first out的栈操作。

  如果用户连续点击Back按键进行退栈操作,最终会退到桌面或者当时启动这个Task的Activity,当所有的Activity都从栈上移除后,task将不复存在。

  当用户离开当前的Task退到桌面时,task进入后台,栈内的全部Activity进入Stop状态,系统维护每个activity的状态,当用户选择应用的启动icon重新唤醒Task时,task重新进入前台,并唤醒最顶上的Activity。

Managing Tasks

  默认的Task管理使用栈的”Last in, first out”特性管理Activity,应用开发还可以根据自己的需要定制这些管理行为,通过设置<activity/>标签的属性和Intent中的flags标记来实现:在新的Task中启动Activity、把已有的Activity从栈内拉到最前台、离开Task时清除栈内的所有Activity,只留下最底下的根Activity等。

  Activity可使用的主要属性:taskAffinity、launchMode、allowTaskReparenting、clearTaskOnLaunch、alwaysRetainTaskState、finishOnTaskLaunch;

  Intent可使用的主要flags:FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_CLEAR_TOP、FLAG_ACTIVITY_SINGLE_TOP;

Defining launch modes

  Launch Mode 定义了一个Activity实例应当怎样关联到当前的Task,有两种设置的方式:Manifest文件和Intent的flags。假设Activity_A要启动Activity_B,B能够在Manifest中定义自己启动的方式,A也能够通过设置Intent的flags定义B启动的方式,如果两者有重复设置的项,A设置的flags优先级更高。Manifest和Intent的flags定义的LauncherMode并没有完全相同,有些只能在Manifest中指定,有些只能在flags中指定。

Using the manifest file
<activity launchMode="mode" .../>

  standard:默认的启动模式,新创建的实例增加到当前Task的顶部,系统直接把Intent传给这个实例,可以有多个实例同时存在;

  singleTop:如果栈顶实例就是当前的Activity,系统将直接把Intent传给onNewIntent()方法,不会再创建一个新实例;如果栈顶实例不是当前Activity,系统会创建一个新实例并添加到栈顶,这种方式允许同一个栈内有多个实例,但是相同的实例不会相邻;在singleTop调用onNewIntent的情况下,用户按Back键后退,顶上的Activity直接退出销毁,而不是退到onNewIntent之前的状态;

  singleTask:Activity在当前的Task里只能有一个实例,如果Activity已经存在了,系统直接把Task切到前台,同时把Intent传给onNewIntent()方法,而不会再创建一个新实例,同时实例顶部的所有Activity都将被弹出。如果Activity由另一个Apk启动,Activity将作为新Task的root元素;

  singleInstance:以这种方式声明的Activity总是在一个新的栈里创建实例,并且是这个Task里唯一的一个元素,任何其他的Activity都不会再添加到这个Task,其他的操作和singleTask相同;

  singleTask启动图解,如果singleTask声明的Activity已经有实例位于后台task之上,此时启动ActivityY将会把整个Task(绿色部分)都切换到前台,并在当前的Task之上,Back操作将会逐个删除Activity,注意这里的叠加不是说把两个栈合并,只是栈的顺序关系。

Using Intent flags

  通过startActivity方法启动Activity时,可以通过指定flags参数修改Activity和Task的默认行为:

  FLAG_ACTIVITY_NEW_TASK:和singleTask的行为相同;

  FLAG_ACTIVITY_SINGLE_TOP:和singleTop的行为相同;

  FLAG_ACTIVITY_CLEAR_TOP:如果启动的Activity在当前Task上已经存在,系统将删除顶部Activity,并调用onNewInstance()方法,这个flags没有等价的launchMode对应。这个flag用在standard的Activity上,除了移除顶部的Activity,系统还会删除原来的Activity并创建一个新的实例。

Handling affinities

  affinity标识了activity应该归属的Task,默认情况下,同一个应用里的activity拥有相同的affinity,所以默认是在同一个Task,如果要修改activity归属的Task,可以通过设置<activity/>标签的 taskAffinity 属性来实现。

  如果启动Activity时设置了FLAG_ACTIVITY_NEW_TASK:系统会先寻找是否有taskAffinity相同的Task存在,如果有就直接在这个Task上启动Activity,如果没有就新建一个Task来启动这个Activity;

  如果Activity的 allowTaskReparenting 属性设置为true:允许Activity从一个Task迁移到另一个Task上,例如,目标Activity默认已经在某个affinity不同的Task上启动,如果此时拥有相同affinity的Task切换到了前台,这个affinity将会从当前的task上平移到同名的这个task上。

Clearing the back stack

  如果用户长时间离开一个Task,系统将会清除Task里的Activity,只留下root activity,当用户重新返回到这个Task时,只有root activity会被恢复,这是系统的默认行为,基于的假设是:用户长时间的离开意味着要废弃当前做的事,当再次回来时会重头再开始做新的事。如果要修改这个默认行为,可以设置下面的3个属性:

  alwaysRetainTaskState:如果这个属性设置为true,系统会抛弃默认行为,保存Task上所有activity的state状态;

  clearTaskOnLaunch:如果Task的root activity把这个属性设置为true,和alwaysRetainTaskState相反,每次用户离开Task后再返回,都会把上面的activity都删除掉,即使只是离开一会儿;

  finishOnTaskLaunch:这个属性只对单个activity生效,如果设置为true,每次Task恢复的时候,都会把Task上设置了这个属性的activity删掉。

Starting a task
<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>

  设置了这两个属性的Activity会在启动页面增加一个入口,用户可以通过这个入口重新回到目标Activity,对于设置了singleTask和singleInstance属性的activity来说,应该要添加ACTION_MAIN和CATEGORY_LAUNCHER的过滤器,否则用户一旦退到后台,将无法再进去这个页面。如果不希望用户退出后再看到某个activity,应该用finishOnTaskLaunch=true来实现。

四、Processes and Application Lifecycle

  Android的程序都是运行在自己的Linux进程里,但是进程本身不直接由程序自己控制,而是由系统综合多个条件来控制:进程本身、对用户的重要程度、系统内存。

  进程的重要程度分级:

  1. foreground process:系统把符合下列条件的进程认为是前台进程:运行的Activity在屏幕的最顶层(onResume状态)、正在执行BroadcastReceiver.onReceive()方法的进程、正在执行service回调的方法的进程(Service.onCreate(), Service.onStart(), or Service.onDestroy())。系统中同一时刻处于前台的进程很少,这个级别的进程只有在系统内存完全不够用的情况下才会被杀死。
  2. visible process:用户能感知到的进程,系统把符合下列条件的进程认为是可见进程:Activity处于对用户可见的状态(onPaused状态)、正在运行后台service的进程( 通过Service.startForeground()执行用户能感知到的任务)、承载了为系统提供特定服务的service(动态墙纸、输入法服务等)。
  3. service process:通过startService()方法启动的服务,例如用于提供上传或者下载的后台任务,这是用户能感知的,当内存不够foreground和visible进程使用的时候会被回收。
  4. cached process:系统在任何时候都可以杀死缓存进程来回收内存,系统缓存这些进程是为了快速切换应用,每个进程都保存了一个或者多个activity实例,正常情况下系统的内存管理都只对这些缓存的进程进行操作。如果进程被杀死后,用户再次回到这个应用,系统可以通过保存的状态恢复Task。回收进程的策略大概是:pseudo-LRU、硬编码限制进程数、限制进程在后台的最长时间等。

      系统根据进程的优先级选择哪些进程可以被杀死,如果一个进程被其他进程所依赖,这个进程的优先级会增加,至少和依赖这个进程的优先级一样高。

五、Parcelables and Bundles

  Parcelable 和 Bundle 被用于跨进程通信,如果 IPC、Binder、Activity间通信、保存配置发生变化时的临时状态。Parcel不是用于普通目的的序列化,所以不要把Pacel数据保存到磁盘或者用于网络传输。

  Sending data between activities:原始数据类型可以直接通过Intent.putExtra()方法保存到Intent中,复杂对象需要先实现Parcelable接口才能通过Intent传输。使用Intent通信时,需要把数据大小控制在几KB内,发送太多的数据提供会抛出TransactionTooLargeException异常。

  Sending data between processes:进程Binder的通信缓存区限制大小为1MB,这个缓存区是进程共享的,包括onSaveInstanceState、startActivity以及其他与系统通信的binder,所以保存在缓存区里的数据块不能太大,超过限制会抛出TransactionTooLargeException的异常,特别的对于savedInstanceState方法,系统进程需要保存这些数据直到用户返回,即使activity进程已经被杀死,官方建议保存的数据应该小于50KB。

六、Recents Screen

  Recents screen是一个系统级别的UI,展示最近访问过的Activity和Task,用户能够快速导航或者删除不需要的Task。系统从5.0开始引入了document-centric模型,允许相同的Activity实例以Task的形式显示在Recents screen中。

Adding Tasks to the Recents Screen

  Using the Intent flag to add a task:Intent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)

  Using the activity attribute to add a task:android:documentLaunchMode=intoExisting/always/none/never

Removing Tasks

  默认情况下,当Activity销毁时,对应的文档Task会自动从Recents screen移除,通过设置Intent的flags或者activity标签的属性,可以修改这种默认行为。

  Using the AppTask class to remove tasks:finishAndRemoveTask(),这个方法会覆盖下面的FLAG_ACTIVITY_RETAIN_IN_RECENTS标记。

  Retaining finished tasks:Intent.addFlags(FLAG_ACTIVITY_RETAIN_IN_RECENTS),将Task保留在Recents screen中,即使对应的Activity结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值