曲靖网站设计公司,品牌营销的定义,推荐网站制作建设书,网站设计是什么Activity作为四大组件之首#xff0c;是使用最为频繁的一种组件#xff0c;中文直接翻译为活动#xff0c;不过如果被翻译为界面会更好理解。正常情况#xff0c;除了Window#xff0c;Dialog和Toast #xff0c; 我们能见到的界面只有Activity。…Activity作为四大组件之首是使用最为频繁的一种组件中文直接翻译为活动不过如果被翻译为界面会更好理解。正常情况除了WindowDialog和Toast 我们能见到的界面只有Activity。
1. 进程模式
APP进程的级别由其活跃的或处于栈顶的组件的活动状态比如生命周期决定。Activity的进程模式分为前台进程、可见进程、Service进程、后台进程、空白进程
前台进程适用于目前操作所需的进程。在不同的情况下进程可能因为其所包含的各种应用组件而被视为前台进程。以下任一条件成立就是前台进程。
当前进程Activity正在与用户进行交互。已调用onResume当前Service正在执行回调的代码 生命周期Service.onCreate() , Service.onStart(), Service.onDestory()进程有一个正在运行的BroadcastReceiverBroadcastReceiver.onReceive()正在执行
可见进程前台进程是一个Dialog进程有一个Service这个Service正在和一个可见的Activity绑定。
Activity在屏幕上对用户可见但是失去了焦点调用了onPause暂时无法操作。Service正在通过Service.startForeground()。实现特定功能的系统服务动态壁纸输入法服务。 服务进程服务进程包含了一个已使用startService()方法启动的Service的进程。 后台进程当Activity的onStop被调用但是onDestroy并没有被调用 空白进程当系统需要内存的时候会暂时将背景进程清除就成为了空白进程。
进程优先级前台进程 可见进程 service进程 后台进程 空进程系统内存不足时会删除优先级低的进程。
2. Activity的生命周期
onCreate表示Activity正在被创建这是生命周期的第一个方法。可以做一些初始化的工作加载界面布局资源初始化Activity所需数据onRestart表示Activity正在被重新启动。一般情况下当当前Activity从不可见更新为可见状态的时候onRestart就会被调用onStart表示Activity正在被启动即将开始。这时Activity已经可见但是没有出现在前台还无法和用户交互了。onResume表示Activity已经可见并且出现在前台并开始活动。要注意这个和onStart的对比。onStart和onResume都表示Activity已经可见但是onStart的时候Activity还在后台onResume的时候Activity才显示到前台。onPause表示Activity正在暂停正常情况下紧跟着onStop就会被调用。在特殊情况下如果快速再回到当前Activity那么onResume会被调用。这种属于特殊情况一般不会发生。在这可以做一些存储数据停止动画等操作但是注意不能太耗时不然会影响到新Activity的显示onPause执行完新的Activity的onResume才会执行。onStop表示Activity即将停止可以做一些稍微重量级的回收工作同样不能太过耗时。onDestroy表示Activity即将被销毁这是生命周期中的最后一个回调。在这里我们可以做一些回收工作和最终资源的释放。
2.1 典型情况下的生命周期
打开新的Activity或者切换到桌面的时候生命周期的回调 onStart 和 onResume、onPause 和 onStop实质有什么不同 假设当前Activity为A如果这时用户打开了一个新的ActivityB那么B的onResume和A的onPause哪个先执行呢
2.2 异常情况下的生命周期
Activity除了受用户操作所导致的正常的生命周期方法调度还有一些异常情况比如当资源相关的系统配置发生改变以及系统内存不足时Activity可能会被杀死。
2.2.1 情况1资源相关的系统配置发生改变导致Activity被杀死并重新创建
首先要对系统的资源加载机制有一定的理解。把一张图片放在drawable目录后可以通过Resources去获取这张图片。同时为了兼容不同的设备我们可能还需要在其他一些目录放置不同的图片比如drawable-mdpi、drawable-hdpi 等等。 这样当应用程序启动的时候系统就会根据设备去加载合适的Resources资源。比如说横屏手机和竖屏手机会**拿到两张不同的图片。突然旋转屏幕由于**系统配置发生了变化在默认情况下Activity就会被销毁并且重新创建当然我们也可以组织重新创建我们的Activity
2.2.2 情况2资源内存不足导致优先级低的Activity被杀死
这种情况不好模拟但是其数据存储和恢复过程和情况1完全一致。这里我们简单描述一下Activity的优先级情况。前台进程 可见进程 服务进程 后台进程 空进程
前台Activity——正在和用户进行交互的Activity优先级最高可见但非前台Activity——比如Activity弹出了一个对话框导致Activity可见但是位于后台无法和用户直接交互。后台Activity——已被暂停的Activity比如执行了onStop优先级最低。 如果一个进程中没有四大组件在执行那么这个进程很快就会被系统杀死。因此一些后台工作不适合脱离四大组件而独自运行在后台中这样进程很容易被杀死。比较好的方法是将后台工作放入Service中从而保证有一定的优先级这样就不会轻易被杀死。 3. Activity的重建机制
重建机制就是在onStop()后调用onSaveInstanceState将Activity的某些状态保存下来。然后在重新创建onStart()之后调用onRestoreInstanceState()把这状态显示出来。
onSaveInstanceState这个方法将要保存的数据 以键值对形式 保存在Bundle对象中。并把保存下来的bundle对象作为参数同时传递给onRestoreInstanceState和onCreate保存了当前Activity的视图结构。API29之后一定会调用在onStop之后onRestoreInstanceState一旦被调用Bundle参数一定有值不用额外判空但onCreate正常启动Bundle参数为null。
3.1 onSaveInstanceState onRestoreInstanceState
主要是 onSaveInstanceState和onRestoreInstanceState方法。系统为我们做了一定的恢复工作。当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity 的视图结构并且在 Activity重启后为我们恢复这些数据。例如文本框TextView中用户输入的数据、ListView滚动的位置等。这些View相关的状态系统都能够默认为我们恢复。针对某一特定的View系统能够为我们恢复哪些数据我们可以查看View的源码。 和Activity一样每个View都有onSaveInstanceState和onRestoreInstanceState。 看一下它们的具体实现就可以知道系统自动为每个View恢复哪些数据。拿TextView来说我们分析一下它保存了哪些数据。
Override
public Parcelable onSaveInstanceState() {Parcelable superState super.onSaveInstanceState();// Save state if we are forced tofinal boolean freezesText getFreezesText();//文本框是否选中boolean hasSelection false;int start -1;int end -1;//如果文本框不为空if (mText ! null) {start getSelectionStart();end getSelectionEnd();if (start 0 || end 0) {// Or save state if there is a selection//表示选中hasSelection true;}}if (freezesText || hasSelection) {SavedState ss new SavedState(superState);if (freezesText) {if (mText instanceof Spanned) {final Spannable sp new SpannableStringBuilder(mText);if (mEditor ! null) {removeMisspelledSpans(sp);sp.removeSpan(mEditor.mSuggestionRangeSpan);}ss.text sp;} else {//保存文本框上的数据ss.text mText.toString();}}if (hasSelection) {// XXX Should also save the current scroll position!ss.selStart start;ss.selEnd end;}if (isFocused() start 0 end 0) {ss.frozenWithFocus true;}ss.error getError();if (mEditor ! null) {ss.editorState mEditor.saveInstanceState();}return ss;}return superState;
}3.2 Activity的configChanges属性
上面分析了系统的数据存储和恢复机制我们知道当系统配置发生改变的时候Activity会被重新创建有没有办法不重新创建呢系统配置中有很多内容如果当某项内容发生改变后我们不想系统重新创建Activity。可以给Activity指定configChanges属性。
android:configChangesorientation如果我们想指定多个值可以用 | 连接起来。
?xml version1.0 encodingutf-8?
manifest xmlns:androidhttp://schemas.android.com/apk/res/androidpackagecom.example.activitytext applicationandroid:allowBackuptrueandroid:iconmipmap/ic_launcherandroid:labelstring/app_nameandroid:roundIconmipmap/ic_launcher_roundandroid:supportsRtltrueandroid:themestyle/Theme.ActivityText activityandroid:name.MainActivity//我们常用的只有localeorientation,keyboardHidden这三个选项android:configChangesorientation|screenSizeandroid:exportedtrue intent-filteraction android:nameandroid.intent.action.MAIN /category android:nameandroid.intent.category.LAUNCHER //intent-filter/activity/application/manifest3.3 重建机制中保存和恢复数据 保存数据 Activity通过onSaveInstanceState()方法来保存状态onSaveInstanceState()执行在onStop()后。然后Activity会委托Window去保存数据接着Window再委托它上面的等级容器去保存数据。顶层容器是一个ViewGroup一般来说可能是一个DecorView。最后顶层容器再去通知它的子元素来保存数据这样整个保存数据过程就完成了。 Activity里面有两个数据结构专门保存状态
View States保存View的状态Instance State存储View states 以及开发者在 onSaveInstanceState中手动保存的Activity成员变量。 恢复数据 通过onRestoreInstanceState方法来恢复状态在保存期间会自动收集View Hieracht视图层次中每一个实现了状态保存和恢复方法的View状态这些数据会在onRestoreInstanceState方法时回传给View并且回传是根据view的id来逐一匹配。 注意事项 为了成功保存状态要求在View内部实现保存和恢复的算法。原生的View都有做到自定义View需要自己来重写实现为了成功恢复状态要给View赋对应的ID如果需要保存Activity的成员变量重写方法时需要保留基类的实现。
4. Activity的启动模式
4.1 LaunchMode standard(标准模式) singleTop(栈顶复用模式) singleTask(栈内复用模式) 如果D所需的任务栈为S1,并且当前任务栈S1的情况为 ADBC根据栈内复用的原则此时D不会重新创建,系统会把D切换到栈而并调用onNewIntent 方法,同时由于singleTask 默认具有clearTop的效果,会导致栈内所有在D上面的 Activity全部出栈于是最终S1中的情况为 AD。这一点比较特殊在后面还会对此情况详细分析。 singleInstance单实例模式
设置启动模式
//通过在Intent中设置标志位来为Activity指定启动模式
Intent intent new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);4.2 复用调用的方法 onNewIntent()
当ActivityA的LaunchMode为SingleTop并且ActivityA在栈顶现在启动ActivityA会调用onNewIntent()方法 。
生命周期顺序为onResume—onPause—onNewIntent—onResume
当ActivityA的LaunchMode为SingleInstance,SingleTask。
如果之前栈内没有它就会去创建一个它的实例也就是会调用 onCreate()→onStart()→onResume()如果之前栈内有它的实例存在的话生命周期的调用变成了onNewIntent()→onRestart()→onStart()→onResume 。只对以上的情况再次启动它们的时候才会调用即只对startActivity有效。如果仅仅从后台切换到前台而不再次启动的情形不会触发onNewIntent。 当ActivityA的LaunchMode为SingleTop并且ActivityA在栈顶现在启动ActivityA会调用onNewIntent()方法 。 生命周期顺序为onResume—onPause—onNewIntent—onResume 当ActivityA的LaunchMode为SingleInstance,SingleTask。 如果之前栈内没有它就会去创建一个它的实例也就是会调用 onCreate()→onStart()→onResume()如果之前栈内有它的实例存在的话生命周期的调用变成了onNewIntent()→onRestart()→onStart()→onResume 。 只对以上的情况再次启动它们的时候才会调用即只对startActivity有效。如果仅仅从后台切换到前台而不再次启动的情形不会触发onNewIntent。 Override
protected void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent); //设置新的intentint data getIntent().getIntExtra(tanksu, 0);//此时的到的数据就是正确的了
}5. Activity的TaskAffinity
我们在上文中多次提到了某个Activity所需的任务栈什么是Activity所需要的任务栈这要从一个参数说起TaskAffinity [afineting] 可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈的名字。如果没有显示指明taskAffinity那么它的taskAffinity就等于Application指明的taskAffinity如果Application也没有指明那么该taskAffinity的值就等于包名。
android:taskAffintycom.ning.task1taskAffinity的作用就是指定想要的任务栈。
当启动模式设置为standard或singleTop时taskAffinity是不起作用的。待启动的 Activity 会跟随源 Activity 的任务栈即使你显式声明了不一样的taskAffinity。当启动模式设置了 singleTask或者 singleInstance时taskAffinity就会新建任务栈来存储待启动的 Activity 实例。
5.1 TaskAffinity和allowTaskReparenting结合
TaskAffinity属性主要是用于和allowTaskReparenting属性配对使用。另外任务栈分为前台任务栈和后台任务栈后台任务栈中的Activity位于暂停状态用户可以通过切换将后台任务栈再调到前台。allowTaskReparenting允许Activity在任务栈之间进行迁移。 具体点来说就是一个Activity现在是处于某个Task当中的但是它与另外一个Task具有相同的affinity值。当另外这个任务切换到前台的时候该Activity就可以转移到切换到前台的这个任务当中。allowTaskReparenting默认是继承至application中的allowTaskReparentingfalse不可以。
当一个应用A启动应用B的某个Activity之后如果这个Activity的allowTaskReparenting属性为true的话。那么当应用B被启动后此Activity会直接从应用A的任务栈转移到应用B的任务栈中。具体一点说就是比如有两个应用A和BA启动了B的一个ActivityC然后按Home 键回到桌面然后再单击B的桌面图标这个时候不是启动了B上的 主Activity而是重新显示了已经被应用A启动的ActivityC。或者说C从A的任务栈转移到了B的任务栈中。
5.2 使用FLAG_ACTIVITY_NEW_TASK标记
单独的FLAG_ACTIVITY_NEW_TASK并不等于singleTask它仅表示寻找Activity所需的任务栈压入。即TaskAffinity指定的任务栈FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_CLEAR_TOP 也不等价于启动模式singleTask当调用startActivity()方法来启动一个Activity时默认是将它放入到当前的Task任务当中。但是如果在Intent中加入了FLAG_ACTIVITY_NEW_TASK 的话情况就会变的复杂起来。首先系统会去检查这个 Activity要求的affinity是否与当前任务栈相同。
如果相同把Activity放到当前的Task当中。如果不同先去检查是否有一个名字与该Activity的Affinity相同的Task。 如果有这个Task被调到前台同时将这个Activity显示到这个Task的顶端。如果没有系统将会尝试为这个Activity创建一个新的Task。需要注意的是如果一个Activity在manifest文件中声明的启动模式是”singleTask”那么他被启动的时候行为模式会和前面提到的指定FLAG_ACTIVITY_NEW_TASK一样。
5.3 任务栈和返回栈
任务栈和返回栈是独立存在的。
任务栈Activity想要的任务栈taskAffinity的作用就是指定想要的任务栈是APP层面的一个东西。
任务栈一般有前台任务栈和后台任务栈之分。比如我们手机中打开了飞书按下Home键之后打开了抖音此时抖音就在前台任务栈飞书就在后台任务栈。
返回栈它是Activity层面的用户页面的返回依赖的是返回栈而不是任务栈。 一个返回栈中可能会包含来自不同任务栈的Activity返回栈就是为了维护正确的回退栈关系。
6. Activity的FLAG
标记位只能在Activity中设置
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);FLAG_ACTIVITY_NEW_TASK这个标记位的作用是为Activity指定singleTask启动模式近似于。 在不设置taskAffinity的情况下单独设置FLAG_ACTIVITY_NEW_TASK并没有任何意义不会创建新的任务栈每次都会创建新的Activity实例不会栈内复用。在一个新的任务栈中启动Activity。如果这个Activity想要的任务栈已经存在并且其中已经运行着待启动的Activity那么这个任务栈就会被带到前台并回调onNewIntent。这个行为和singleTask一致。 FLAG_ACTIVITY_SINGLE_TOP这个标记位的作用是为Activity指定singleTop启动模式其效果和在XML中指定该启动模式相同 FLAG_ACTIVITY_CLEAR_TOP具有此标记位的Activity当它启动时在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起出现。如果此时被启动的Activity采用standard模式启动那么它连同它之上的Activity都要出栈系统会创建新的Activity实例并放入栈顶 NEW_TASKCLEAR_TOP ! singleTask因为在standard模式下启动会把栈内本身的Activity也删除所以不等于signleTask。 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS具有这个标识位的Activity不会出现在历史Activity的列表中当某些情况下我们用户通过历史列表回到我们的Activity的时候这个标记比较有用。他等同于在XML中指定Activity属性
android:excludeFromRecentstrueFLAG_ACTIVITY_NO_HOSTORY被指定的Activity在跳转到其他Activity后将它从任务栈中移除。
7. IntentFilter
我们知道启动Activity一般有显示调用和隐式调用两种。显示调用需要明确指定被启动对象的组件信息包括包名和类名。而隐式调用则不需要。这里简单介绍一下隐式调用隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息。
只有一个Intent同时匹配了action类别、category类别、data类别才算完全匹配只有完全匹配才能启动目标Activity。一个Activity可以有多个IntentFilter一个Intent只要能匹配任何一组IntentFilter即可成功启动对应的Activity。
8. Activity的数据传输
使用Intent的putExtra传递传递数据大小有限制1M左右根据BinderSharedPreference进行数据传递使用静态变量传递数据使用Bundle对象使用Activity销毁时传递数据onActivityResult使用序列化对象Seriazable
9. Activity的finish()
finish()用于结束Activity进程关闭Activity但不关闭栈。调用finish()方法后先pause()当前Activity会通过AMS的ApplicationThread.scheduleDestroyActivity方法向ActivtyThread主线程发送一个H.DESTROY_ACTIVITY消息主线程会调用handleDestroyActivity来处理这个消息在通过层层调用回调Activity的onDestroy方法。业务场景比如在订单列表A点击新建订单进入新建订单ActivityB中点击保存后跳转到订单详细C中。在C中我们点击返回键要求返回到A中。
10. 相关问题
10.1 进程模式相关问题
何谓进程模式
10.2 生命周期相关问题
onPause() 和 onStop()的区别 A启动B然后B退回A两者的生命周期变化 为什么先Activity的onResume()执行然后才是调用源Activity的onStop()方法呢? MainActivity跳转到DialogActivity(盖不住MainActivity)再跳转到secondActivity的生命周期 从secondActivity返回到DialogActivity的生命周期 弹出Dialog对Activity的生命周期有什么影响
10.3 重建机制相关问题
onSaveInstanceState方法调用的时机 什么时候会发生重建 为什么会有重建机制 优先级较低的Activity在内存不足的被回收后怎样恢复到销毁前的状态
10.4 启动模式相关问题
Activity的启动模式A是standardB是singleInstance, A启动B又启动A会有几个对象 启动模式和FLAG的区别
10.5 Activity和application有什么区别
Activity是UI界面的抽象而application是应用程序的抽象。两者都是Context的子类。应用程序每次启动的时候系统会为其创建一个application对象且只有一个单例类用来存储一些系统的信息相当于一个容器。启动application时系统会创建一个PID进程ID所有的Activity都在这个进程上运行在application创建时会初始化一个全局变量同一个应用的activity都可以获取到这个变量。
10.6 onStop和onDestroy()回调延时及延时10S的问题
为什么回调会延时
Activity调用流程打开ActivityA - 打开ActivityB - 关闭ActivityB - 回到ActivityA由于要关闭的Activity B或者要打开的Activity A 往主线程的MessageQueue中连续不断地post大量的msg。导致主线程一直在不断地进行消息循环处理 没有停歇。 因此APP不能向AMS发起IPC来进行ActivityB的销毁。所以finish ActivityB之后onDestroy不会被及时回调。具体延时多久要看主线程中堆积的msg什么时候被处理完。
延时10S Android系统安排了一套流程来保证即使正常流程被阻断以后ActivityB还是能被销毁。 在关闭Activity B返回Activity A的时候当AMS侧发起 IPC通知 APP侧的Activity A执行resume的时候同时也会向AMS自己的主线程发送一个msg该msg延时10s后执行。 该msg的具体内容与 正常流程APP空闲时段需要执行的任务一致当然也包括销毁B。 这样Activity B的onDestroy方法也就在延时10s后调用执行。 我们就该在写代码的时候及时关闭、清理、移除不必要的主线程消息并且尽可能的保证每个消息处理时间不要太长。