【Android】Activity
Activity介绍
- 概念:Activity 是应用程序与用户进行交互的入口,代表单一的界面。每个 Activity 通常对应一个屏幕,例如登录界面、列表界面等。
- 用途:展示用户界面;承载UI控件并管理用户交互。
Activity的创建
-
定义布局:在 res/layout 目录下定义的布局文件,通过 XML 进行界面设计。
-
定义Activity类
- 一个 Activity 是由继承自 android.app.Activity 或其子类的类定义的。创建 Activity 的基本代码结构如下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
- AppCompatActivity一种兼容性更好的 Activity 基类,可以支持较老版本的 Android。
- 使用 setContentView(R.layout.activity_main) 来设置 Activity 的用户界面xml布局文件。
-
注册Activity
在 AndroidManifest.xml 中通过 标签注册 Activity。如果这是应用的启动界面,需要添加 ,指定 MAIN 动作和 LAUNCHER 类别。<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
```
其他属性:
- android:exported:指定是否可以由应用外部的组件启动。
- android:label:设置 Activity 的标签,通常在系统任务管理器和桌面图标上显示。
- android:icon:指定 Activity 的图标。
- android:theme:为 Activity 指定一个主题。
- android:launchMode:指定 Activity 的启动模式。
- android:configChanges:用于指示在配置更改(如屏幕旋转、语言更改等)时,Activity 是否自行处理这些变化。
android:screenOrientation:指定 Activity 的屏幕方向。portrait | landscape- Android 8.0开始,android:screenOrientation属性已经被弃用,取而代之的是使用androidx.core:core库中的androidx.core:core-ktx库中的androidx.core.content.ContextCompat.getSystemService方法来获取WindowManager服务,然后使用WindowManager.LayoutParams.ORIENTATION_LOCK_SENSOR来锁定屏幕方向
- android:windowSoftInputMode:控制软键盘的交互模式,影响其显示和调整方式。
当有焦点产生时,软键盘是隐藏还是显示:- stateUnspecified:默认。系统会根据界面采取相应的软键盘显示模式。例如,如果界面上只有文本和按钮而没有输入框,软键盘通常不会自动弹出;如果界面上有获取了焦点的输入框,或者界面有滚动需求(如包含RecyclerView、ScrollView等可滑动的控件),软键盘可能会自动弹出
- stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示
- stateHidden:用户选择activity时,不管当前界面是否有输入需求,软键盘总是被隐藏
- stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏
- stateVisible:软键盘通常是可见的。当用户导航到该Activity的主窗口时,软键盘可能会自动弹出
- stateAlwaysVisible:用户导航到该Activity时,软键盘会自动弹出,即使在界面上没有输入框的情况下也会强制显示
是否减少活动主窗口大小以便腾出空间放软键盘: - adjustUnspecified:默认。没有指明是否减小Activity主窗口的大小来为软键盘预留空间,或者是否当Activity主窗口的部分内容被软键盘遮盖时其内容焦点是可见的。系统会根据窗口内容里是否有可以滑动的布局视图来选择一种模式:如果有这样的视图,Activity的窗口将会调整大小;如果没有,则可能不会调整大小。
- adjustResize:Activity的主窗口总是调整大小来为软键盘预留空间。如果界面中有可滑动控件,显示效果可能与未指定调整方式类似;如果界面中没有可滑动控件,软键盘可能会盖住一些控件(但布局的位置不会发生变化)
- adjustPan:Activity的主窗口并不会调整大小来为软键盘预留空间。相反,当前窗口的内容将会自动平移,以便当前焦点从不被软键盘遮盖。这样用户总能看到输入内容的部分。但需要注意的是,如果用户想与窗口中被遮挡的部分交互,可能需要先关闭软键盘
- adjustNothing:布局不发生任何变化,键盘覆盖在布局上
- android:allowTaskReparenting:指示是否允许 Activity 在不同任务之间切换。
- android:clearTaskOnLaunch:指示在启动此 Activity 时是否清空任务栈。只对根Activity生效。
值为true时:如果用户从主屏幕的应用图标启动该Activity,会清除任务栈中的所有Activity,并把该Activity作为新任务栈的根Activity。 - android:taskAffinity:指定此 Activity 相关联的任务栈。
- android:noHistory:当用户离开 activity 且屏幕上不再显示该 activity 时,是否应从 activity 堆栈中将其移除并通过调用 finish() 方法来完成 activity。默认值为 “false”。
“true” 表示它不会留在任务的 activity 堆栈内,因此用户将无法返回该 activity。在此情况下,如果您通过启动另一个 activity 来获取该 activity 的结果,系统永远不会调用 onActivityResult()。
Activity栈(任务栈)
- 用于存放一组Activity,进行交互的Activity位于任务栈栈顶。
- 当位于前台的Activity因为各种原因被销毁时,该Activity出栈,处于栈第二层的Activity被激活,上浮到栈顶。
- 直到栈为空时,系统就会回收这个栈。
- 一个Activity在栈中的位置变化反应了它在不同状态的转换。
- 除了栈顶处于Active状态的Activity外,其他Activity都有可能在系统内存不足时被回收。
taskAffinity
一个task是一组相关的Activity的集合,它们共享一个任务栈。通过设置Activity的taskAffinity属性,可以指定Activity所属的任务栈
设置方式:
- 在AndroidManifest.xml文件中通过android:taskAffinity属性设置Activity的TaskAffinity。
<activity
android:name=".MainActivity"
android:taskAffinity="com.example.task1" />
<activity
android:name=".SecondActivity"
android:taskAffinity="com.example.task2" />
- 在代码中通过setTaskAffinity(String affinity)方法动态设置Activity的TaskAffinity。
val intent = new Intent(this, SecondActivity.class)
intent.setTaskAffinity("com.example.task1")
startActivity(intent)
Activity启动模式
启动模式用于定义一个 Activity 在栈中的行为,通过设置Activity的launchMode参数来设置。
standard【默认】
每次启动都会创建新的实例。
- 每当启动一个新的Activity,它就会在返回栈中入栈,并处于栈顶。
- 系统不关注这个Activity是否已经在返回栈中存在。
- 在当前模式下启动当前Activity,开启的是一个新的实例。
- 多个同Activity实例在栈顶需要多次back才能退出。
适用场景:适用于大多数普通Activity场景,比如新闻详情页、商品详情页等,每次点击新内容时一个新的Activity实例会被创建。
singleTop: 栈顶复用
如果当前栈顶已经存在该 Activity,则不会创建新实例,而是复用该实例并调用其 onNewIntent() 方法。
启动时检查栈顶:
- 如果Activity已经在栈顶,就不再创建新的Activity。
- 如果栈顶不是该Activity,就创建新的实例,不在乎栈中是否有该Activity的其他实例。
适用场景:适用于防止短时间内多次启动相同Activity的情况,比如新闻应用的主页,当在主页上重复点击同一个新闻时,不需要再创建新的主页实例。
singleTask: 栈内复用
保证该 Activity 在任务栈中只有一个实例。
要区分要启动的Activity的所需栈是否是当前栈,启动时检查返回栈中是否有该Activity存在
- 如果存在直接使用该实例,并把返回栈中该实例之上的其他Activity统统出栈。
- 如果没有就创建新的Activity实例。
一个返回栈中只会有一个该Activity的实例。不同程序使用不同的Activity实例。
适用场景:适用于希望某个Activity在应用中唯一存在的情况,如应用的主屏幕、登录页面等。在启动其他功能时,该主屏幕或登录页面应该作为中介,因此需要保持唯一实例。
singleInstance: 单实例模式
独占一个任务栈,保证只有一个实例存在。
启用一个新的返回栈来管理这个Activity,不同程序共享这个Activity实例。
eg:如果Activity2是SingleInstance,先后启动Activity1, Activity2, Activity3,在Activity3 back会直接回到Activity1,因为Activity2不在当前返回栈。再次back,当前返回栈空,显示另一个返回栈的栈顶,即Activity2。再次back,所有返回栈空,退出程序。
适用场景:适用于某些需要完全独立于应用正常任务栈的情况,比如用户接受电话等应用场景,需要从当前任务跳脱出来并确保这一独特Activity唯一存在。
Activity状态
基本状态
- Active/Running
一个新 Activity 启动入栈后,它显示在屏幕最前端,处于栈的最顶端(Activity栈顶),此时它处于可见并可和用户交互的激活状态,叫做活动状态或者运行状态 - Paused
当 Activity失去焦点, 被一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶,此时的状态叫做暂停状态(Paused)。此时它依然与窗口管理器保持连接,Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被强行终止掉。所以它仍然可见,但已经失去了焦点故不可与用户进行交互。 - Stopped
如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被强行终止掉。 - Killed
如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,系统采用两种方式进行删除,要么要求该Activity结束,要么直接终止它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。
状态切换
当一个 Activity 实例被创建、销毁或者启动另外一个 Activity 时,它在这四种状态之间进行转换,这种转换的发生依赖于用户程序的动作
Activity生命周期
Android 中的 Activity 生命周期由多个回调方法控制,用于处理应用在不同状态下的行为。
- onCreate(): 初始化界面时调用,整个生命周期只调用一次,此时Activity不可见。
- 加载界面布局文件(调用 setContentView())。
- 初始化界面控件、数据以及一些必要的资源。
- 进行初步的逻辑设置和监听器的绑定。
- onStart(): Activity变为可见,但尚未和用户交互时调用。
- 可以进行一些准备工作,比如启动动画、检测权限等。
- onResume(): Activity 位于前台并开始与用户交互。
- 通常用于恢复暂停的操作,例如重启相机、音频播放或动画。
- 注册需要在用户可见时生效的监听器(如传感器监听器、广播接收器等)。
- onPause(): Activity 正在离开前台(可能被新 Activity 覆盖或回到后台),但依然部分可见时调用;用户无法交互。
- 暂停可能影响用户体验的操作,比如停止动画或暂停视频播放。
- 保存一些即时的但不永久的数据,比如用户输入的部分内容。
- 释放某些资源,如 GPS、摄像头等,以节省电量。
- onStop(): Activity 完全不可见,进入后台。
- 释放占用大量内存或 CPU 资源的操作,如取消网络请求、保存数据到本地数据库。
- 如果应用需要进入后台运行,可以在这里处理一些适应后台的工作。
- onRestart():当 Activity 从停止状态重新变为可见时被调用。
- 进行重新初始化的一些工作,可以恢复一些在 onStop() 中释放的资源。
- 此方法常与 onStart() 搭配使用,确保重回前台时的设置。
- onDestroy(): Activity 即将被销毁(用户手动退出、系统回收内存时)。
- 释放所有已分配的资源,以避免内存泄漏。
- 终止后台运行的任务、线程。
- 通常用于清理一些需要彻底关闭的内容,如数据库连接、服务解绑等。
总结
- 启动阶段(onCreate()、onStart()、onResume()):加载界面和数据、注册监听器、恢复资源。
- 可见阶段(onResume()):用户与界面交互,执行关键功能。
- 暂停阶段(onPause()、onStop()):释放不必要的资源、保存瞬时数据。
- 销毁阶段(onDestroy()):彻底清理资源,防止内存泄漏。
生命周期受哪些行为的影响
- 正常的用户交互:摁Home键、摁返回键、摁按钮跳转页面
- 资源相关的系统配置发生改变:如横竖屏切换、系统语言切换等,会导致Activity被杀死并重新创建。
- 如果不想Activity被重建:在AndroidManifest.xml中为Activity添加属性configChanges = xxx,代表xxx模式下activity不会重新创建。
- 重写activity的onConfigurationChanged方法,在activity发生xxx配置更改时onConfigurationChanged就会回调,这里我们可做一些监听工作。
- 系统内存不足:资源内存不足导致低优先级的Activity被杀死
Activity优先级,Activity按照优先级从高到低,可分为如下三种情况:
(1)前台Activity——正在和用户交互的Activity,优先级最高
(2)可见但非前台——Activity可见,但是位于后台不能交互
(3)后台Activity——已经暂停了Activity,比如执行了onStop,优先级最低
异常情况下数据的存储
activity提供两个回调方法用来进行异常情况下数据的存储、恢复。
- onSaveInstanceState(Bundle outState)
activity异常终止时系统会调用此方法来保存activity的状态,例如记录界面上输入框的文字、ListView滑动位置,重建时直接恢复这些状态
activity异常终止时我们也可以在这个方法中添加逻辑操作,存储我们想要的信息。
注意:这个方法在activity正常终止是不会回调的。 - onRestoreInstanceState(Bundle savedInstanceState)
当activity异常终止重建时,系统会回调此方法,并把activity销毁时onSaveInstanceState()方法所保存的Bundle 对象作为参数传递给onCreate()和onRestoreInstanceState()
也可以通过onCreate和onRestoreInstanceState方法判断activity是否被重建。因为正常情况下Activity的onCreate方法的Bundle参数为null。异常情况下Bundle才非空。
注意事项:
- 对于有大量初始化操作的 Activity,可以将部分初始化逻辑放在 onStart() 或 onResume() 中,以便更快显示界面。
- 避免在 onPause() 和 onStop() 中释放重要资源,导致恢复后出现问题。
- 在 onCreate()、onResume() 等方法中不要执行耗时操作,避免阻塞主线程,导致界面卡顿。耗时操作应该放到后台线程中(如使用 AsyncTask、Handler 或 Coroutines 等)。
- Activity 的生命周期会受到很多外部因素影响,比如来电、通知、切换应用等。需要正确处理 onPause() 和 onStop() 事件,保证用户在返回应用时获得无缝的体验。
- 配置变更(如语言、屏幕大小、方向变化)会导致 Activity 被重建,因此需要特别处理,例如保留用户数据、保存状态(通过 onSaveInstanceState() 和 onRestoreInstanceState())。
- Activity 的生命周期较短,如果持有长生命周期对象(例如静态变量或未解除的 Handler),可能会导致内存泄漏。可以使用弱引用(WeakReference)或者在 onDestroy() 中确保解除引用来避免这种情况。