字节Android工程师都在学习的Activity与Activity调用栈,你都学习了?
本文为Android的Activity相关知识整理,具体参考了
- 《Android开发艺术探索》第一章
- 《Android群英传》第八章
- 《第一行代码》第二章
Activity是与用户交互的第一接口。
- Activity生命周期
Activity具有多种形态。其生命周期图如下:
其中,有三个稳定态,其他都是过渡态:
- Resumed ,此时,Activity处于栈顶,处理用户交互。
- Paused,当Activity的一部分被挡住的时候进入这个状态,不会与用户交互。
- Stopped,当Activity完全被覆盖时进入这个状态,此时Activity不可见,仅在后台运行。
1.1 Activity经典生命周期
(以下均指MainActivity的生命周期)
- Activity启动,点开一个应用展示MainActivity
- onCreate()->onStart()->onResume() 到达Resumed状态,此时MainActivity被打开。
- Activity暂停与恢复,1:打开了一个半屏幕的dialog,2:按返回键返回MainActivity
- onPause()到达Paused状态,此时dialog被打开。
- onResume()到达Resumed状态,此时dialog被关闭,显示MainActivity
- Activity停止与恢复,1:打开另一个SecondActivity,2:按返回键返回MainActivity
- onPause()->onStop()到Stopped状态,此时SecondActivity被打开。
- onRestart()->onStart()->onResume()到Resumed状态,此时SecondActivity被关闭,重新回到MainActivity。
- Activity销毁,在MainActivity按返回键
- onPause()->onStop()->onDestory(),此时已经销毁 MainActivity。
注意:
- 在onPause()中一定要释放使用的系统资源,比如Camera,sensor,receivers!!
- 在onStop()中执行更大、更多CPU密集的关闭操作。比如写入信息到数据库。
- 当系统长期处于onStopped状态而且此时系统内存紧张时,系统会回收此Activity,而此时,系统会通过onSaveInstanceState()方法将Activity状态保存到Bundle对象中(finish()方法销毁时不会保存)。当重新创建此Activity时,保存的Bundle对象会传递到onRestoreInstanceState()与onCreate()中。
- onCreate和onDestroy是配对的,分别标识着Activity的创建和销毁; onStart和onStop是配对的,标识着Activity是否可见;onResume和onPause是配对的,标识着Activity是否在前台。
- 假设当前Activity为MainAcitivty ,这时用户启动SecondActivity ,那么MainAcitivty 的onPause先执行,SecondActivity 的onResume后执行。Android规定,不能在onPause中做重量级操作,就是基于这里。我们应该尽量在onStop中做操作,使得新Activity尽快显示
- MainAcitivty->onPause
- SecondActivity->onCreate
- SecondActivity->onStart
- SecondActivity->onResume
- MainAcitivty->onStop
1.2 异常生命周期
以下几种情况下,Activity的生命周期会发生异常 1,资源相关的系统配置发生改变 比如,旋转屏幕,在默认状态下,Activity就会被销毁并且重新创建。
- 首先,Activity就会被销毁,onPause(), onStop(),onDestory()均会被调用,同时由于Activity是在异常状态下终止的,系统会在onStop()之前,调用onSaveInstanceState()来保存当前Activity的状态。
- 然后,Activity被重新创建,系统会在onStart()之后,调用onRestoreInstanceState(),并且把之前保存的Bundle对象传递给onRestoreInstanceState()和onCreate()方法。
- PS: 这两个方法都能对Bundle数据进行处理,但是一般用 onRestoreInstanceState()。因为onRestoreInstanceState()一旦被调用,其参数 Bundle savedInstanceState 一定是有值的,我们不必判断其是否为空。
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
String test = savedInstanceState.getString("extre_test");
Log.d(TAG, test);
}
}
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString("extre_test", "test");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
String test = savedInstanceState.getString("extre_test");
Log.d(TAG, test);
}
执行顺序: 1 onPause() 2 onSaveInstanceState(Bundle outState) 3 onStop() 4 onDestory() 5 onCreate(Bundle savedInstanceState) 6 onStart() 7 onRestoreInstanceState(Bundle savedInstanceState)
注意:系统仅仅在Activity异常终止时才会调用onRestoreInstanceState()。
2,系统内存不足 此时的系统中数据的存储和恢复情况和上面第一种情况一致。Activity优先级如下:
1 前台Activity 2 可见但非前台Activity 3 后台Activity 当系统资源不足的时候,会按照这个优先级使用onSaveInstanceState()和onRestoreInstanceState()来存储和恢复数据。
还有一些后台进程不是四大组件,这样就会很容易被杀死~~ 一般是将这些后台工作放入Service中,从而保证有一定的优先级。
PS:阻止Activity被重新创建
使用android:configChanges="orientation|screenSize"属性。
常用属性:
- locale:设备的本地位置发生变化,一般指切换了系统语言。
- orientation:屏幕方向发生变化
- screenSize:屏幕大小发生变化,当旋转屏幕的时候,屏幕尺寸会变!!!!这个比较特殊,当minSdkVersion和targetSdkVersion均低于13时,此选项不会导致Activity重启,否则会导致Activity重启。
- keyboardHidden:键盘的可访问性发生变化,比如调出键盘。
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|keyboardHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
......//当系统配置发上变换时,进行的工作
}
- Android任务栈
Android的APP通常会有多个Activity,各个Activity之间通过Intent进行连接,系统通过栈结构来保存整个APP的Activity。系统有两种方式控制Activity的启动模式。
- AndroidMainifest的launchMode
- Intent Flag(优先级更高)
2.1AndroidMainifest启动模式
在AndroidMainifest.xml文件里面的activity标签设置启动模式。
<activity
android:name=".FirstActivity"
android:launchMode="singleTop"
android:label="This is FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- standard,标准模式,每次都会创建新的Activity覆盖在原Activity上
- singleTop,栈顶复用模式,首先判断栈顶Activity是否是要启动的Activity,如果是则不创建新的Activity而直接引用这个Activity;如果不是则创建新的Activity。
- singleTask,栈内复用模式,检测整个Activity栈中是否存在当前需要启动的Activity,如果存在则将该Activity置于栈顶,并销毁其上所有Activity。
- singleInstance,单实例模式,创建新的任务栈,且该任务栈仅有一个Activity。
TaskAffinity taskAffinity,任务相关性。xml中的一个属性,标识了一个Activity所需要的任务栈的名字。默认是包名。如果设置了其他的名字如com.test.task1,那启动它的时候就会新建一个名为com.test.task1的任务栈。
<activity
android:name="com.test.task0.MainActivity"
android:label="@string/app_name"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category andorid:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
andorid:name="com.test.SecondActivity"
android:taskAffinity="com.test.task1"
android:label="@string/app_name"
android:launchMode="singleTask"/>
<activity
andorid:name="com.test.ThirdActivity"
android:taskAffinity="com.test.task1"
android:label="@string/app_name"
android:launchMode="singleTask"/>
如果从MainActivity启动SecondActivity,然后再启动ThirdActivity,那么任务栈如下: com.test.task0 MainActivity com.test.task1 SecondActivity ThirdActivity 若再从ThirdActivity启动MainActivity,那么任务栈如下: com.test.task0 MainActivity com.test.task1 SecondActivity ThirdActivity MainActivity
2.2 Intent Flag启动模式
对Intent进行设置
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
重要的Flag:
- FLAG_ACTIVITY_NEW_TASK,启动的Activity在新的Task中,相当于android:launchMode="newTask"
- FLAG_ACTIVITY_SINGLE_TOP,相当于android:launchMode="singleTop"
- FLAG_ACTIVITY_CLEAR_TOP,相当于android:launchMode="singleTask"
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,当以此种模式启动A,A再启动B时,A会被销毁。等同于android:excludeFromeRecents="true"
3 IntentFilter的匹配规则
<activity android:name="SecondActivity">
<intent-filter>
<action android:name="android.intent.action.SEND">
<category android:name="android.intent.category.DEFAULT">
<data android:mimeType="text/plain">
</intent-filter>
</activity>
调用一个Activity主要包括两种: 1,显式调用。
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
2, 隐式调用
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(Uri.parse("file://abc"), "text/plain");
startActivity(intent);
隐式调用需要Intent能够匹配目标组件的IntentFilter中的过滤信息。 IntentFilter中的过滤信息有action, category, data。 只有一个Intent同时匹配这三个类别才能启动目标Activity。
- action的匹配要求,Intent中的action存在且必须和过滤规则中的其中一个action相同。
- data的匹配要求,Intent中的data存在且必须和过滤规则中的其中一个data相同。
- category匹配要求,如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。也就是说它的category可以没有!!!!!(原因是默认的category是android.intent.category.DEFAULT,不过得在activity标签的intent-filter中加入android.intent.category.DEFAULT这个category)
- 退出程序的两种方法
4.1 利用Activity的singleTask模式退出
- 将主Activity设置为singleTask模式
<activity android:name=".MainActivity" android:launchMode="singleTask" ...
- 在要退出的Activity中转到主Activity中,从而将主Activity之上的Activity都清除
Intent intent = new Intent(this, MainActivity.class); startActivity(intent);
- 然后重写主Activity的onNewIntent()方法,在方法中加上finish(),从而销毁最后一个Activity。
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); finish(); }
4.2 利用专门的集合类对所有的活动进行管理
- 创建一个ActivityCollector 类作为活动管理器。List<Activity>作为存放活动的列表。
public class ActivityCollector { public static List<Activity> activities = new ArrayList<Activity>(); public static void addActivity(Activity activity) { activities.add(activity); } public static void removeActivity(Activity activity) { activities.remove(activity); } public static void finishAll() { for (Activity activity : activities) { if (!activity.isFinishing()) { activity.finish(); } } } }
- 创建一个继承自Activity的BaseActivity 。ActivityCollector里存放的活动要随着onCreate而添加,随着onDestory而销毁。
public class BaseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d("BaseActivity", getClass().getSimpleName()); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } }
- 全部的Activity都要继承自BaseActivity,此时,只要调用
ActivityCollector.finishAll();
,就可以销毁所有Activity,关掉程序。
相关文章
- LibreOffice 7.5 发布:漂亮的新应用图标和酷炫功能
- elementary OS 7 发布
- Windows 应用兼容层 Wine 8.1 发布:默认启用“Windows 10”前缀
- 微软正测试新功能:当 Windows 11 有新的小组件可用时会提醒通知
- 解析分布式存储选型和应用九个典型问题
- ClickHouse在自助行为分析场景的实践应用
- Chrome DevTools 远程调试安卓网页的原理
- Uni-app + Vue3 页面如何跳转及传参?
- 微软证实系统还原点会损坏 Windows 11 22H2 版本应用程序
- 巧用 Transition 实现短视频 APP 点赞动画
- 初学者试试,HarmonyOS应用开发者基础认证
- 媒体实测微软 Windows 开发工具包 2023:存在不兼容 HDR 显示器、某些应用无法运行等问题
- 快速了解Navigator API SetAppBadge
- 微软 Windows 11 Dev 预览版 Build 25276 发布,应用兼容问题对话框 UI 改进
- 基于Next.js、Prisma、Postgres和Fastfy构建全栈APP
- 开始菜单搜索框变圆角,微软 Windows 11 Beta 预览版 22621.1095 和 22623.1095 发布
- 2022-2023 十大应用开发趋势
- 观远数据发布业内首部《移动BI白皮书》,深入业务数字化场景重新定义移动BI
- Windows 10 学院:不借助第三方工具如何卸载 Windows 10 预装应用
- 正处高质量发展期,我国大数据产业突破1.3万亿元