【Android 源码分析】Activity生命周期之onDestroy

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的IT男

本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815

正文

生命周期系列:

  • Activity生命周期之onPause

  • onCreate,onStart,onResume-1

  • onCreate,onStart,onResume-2

  • Activity生命周期之onStop-1

  • Activity生命周期之onStop-2

  • Activity生命周期之onDestory

生命周期分析系列剩下最后一个 onDestroy 了,但是之前分析的桌面冷启动案例没有 onDestroy 这一步,所以写了个App, 有2个 Activity ,操作为:
MainActivity 启动 MainActivity2 ,然后按back键,这样就会触发 MainActivity2 的 onDestroy。

对应的Events日志如下:

// MainActivity2 触发 finish (writeWmFinishActivity)
04-02 19:11:46.665 13550 18317 I wm_finish_activity: [0,105165904,12,com.example.myapplication/.MainActivity2,app-request]
//  MainActivity2 触发 pause
04-02 19:11:46.671 13550 18317 I wm_pause_activity: [0,105165904,com.example.myapplication/.MainActivity2,userLeaving=false,finish]
//  MainActivity2 应用端执行生命周期onPause
04-02 19:11:46.680 19818 19818 I wm_on_paused_called: [105165904,com.example.myapplication.MainActivity2,performPause]
//  MainActivity2 添加到stop计划
04-02 19:11:46.681 13550 18317 I wm_add_to_stopping: [0,105165904,com.example.myapplication/.MainActivity2,completeFinishing]
04-02 19:11:46.687 13550 18317 I wm_set_resumed_activity: [0,com.example.myapplication/.MainActivity,resumeTopActivity]
04-02 19:11:46.700 13550 18317 I wm_resume_activity: [0,183091121,12,com.example.myapplication/.MainActivity]
04-02 19:11:46.713 19818 19818 I wm_on_restart_called: [183091121,com.example.myapplication.MainActivity,performRestartActivity]
04-02 19:11:46.714 19818 19818 I wm_on_start_called: [183091121,com.example.myapplication.MainActivity,handleStartActivity]
04-02 19:11:46.715 19818 19818 I wm_on_resume_called: [183091121,com.example.myapplication.MainActivity,RESUME_ACTIVITY]
04-02 19:11:46.715 19818 19818 I wm_on_top_resumed_gained_called: [183091121,com.example.myapplication.MainActivity,topWhenResuming]
// MainActivity2 触发 destroy
04-02 19:11:47.092 13550 13571 I wm_destroy_activity: [0,105165904,12,com.example.myapplication/.MainActivity2,finish-imm:idle]
// MainActivity2 执行 onStop
04-02 19:11:47.128 19818 19818 I wm_on_stop_called: [105165904,com.example.myapplication.MainActivity2,LIFECYCLER_STOP_ACTIVITY]
// MainActivity2 执行 onDestroy
04-02 19:11:47.130 19818 19818 I wm_on_destroy_called: [105165904,com.example.myapplication.MainActivity2,performDestroy]

现在开始消消乐

04-03 15:02:41.806 31569 31732 D AutofillManagerService: onBackKeyPressed()
04-03 15:02:41.808 31569 32057 V WindowManager: Finishing activity r=ActivityRecord{6dbc456 u0 com.example.myapplication/.MainActivity2} t8}, result=0, data=null, reason=app-request
04-03 15:02:41.808 31569 32057 I wm_finish_activity: [0,115065942,8,com.example.myapplication/.MainActivity2,app-request]

1. 应用端触发finish – wm_finish_activity

按back键是会触发 Activity::finish 方法的,所以从这里开始跟流程

# Activitypublic void finish() {finish(DONT_FINISH_TASK_WITH_ACTIVITY);}private void finish(int finishTask) {......if (ActivityClient.getInstance().finishActivity(mToken, resultCode, resultData,finishTask)) {mFinished = true;}......}# ActivityClientpublic boolean finishActivity(IBinder token, int resultCode, Intent resultData,int finishTask) {try {return getActivityClientController().finishActivity(token, resultCode, resultData,finishTask);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

然后就要开始跨进程交给 SystemService 端处理了

2. SystemService 端处理

# ActivityClientController/*** This is the internal entry point for handling Activity.finish().** @param token      The Binder token referencing the Activity we want to finish.* @param resultCode Result code, if any, from this Activity.* @param resultData Result data (Intent), if any, from this Activity.* @param finishTask Whether to finish the task associated with this Activity.* @return Returns true if the activity successfully finished, or false if it is still running.*/@Overridepublic boolean finishActivity(IBinder token, int resultCode, Intent resultData,int finishTask) {......final long origId = Binder.clearCallingIdentity();Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");try {......// 主流程r.finishIfPossible(resultCode, resultData, resultGrants,"app-request", true /* oomAdj */);res = r.finishing;......} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);Binder.restoreCallingIdentity(origId);}......}   
# ActivityRecord@FinishRequest int finishIfPossible(int resultCode, Intent resultData,NeededUriGrants resultGrants, String reason, boolean oomAdj) {// log ProtoLog.v(WM_DEBUG_STATES, "Finishing activity r=%s, result=%d, data=%s, "+ "reason=%s", this, resultCode, resultData, reason);......try {mTaskSupervisor.mNoHistoryActivities.remove(this);// 重点* 1. 设置为正在 finish makeFinishingLocked();// Make a local reference to its task since this.task could be set to null once this// activity is destroyed and detached from task.final Task task = getTask();// 重点* 2. 第一个events :wm_finish_activityEventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this),task.mTaskId, shortComponentName, reason);......// 重点* 3. 返回ResultfinishActivityResults(resultCode, resultData, resultGrants);......// Tell window manager to prepare for this one to be removed./ 重点* 4.设置不可见setVisibility(false);if (getTaskFragment().getPausingActivity() == null) {// 重点* 5. 打印2个logProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this);if (DEBUG_USER_LEAVING) {Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");}// 重点* 6.开始执行pause逻辑getTaskFragment().startPausing(false /* userLeaving */, false /* uiSleeping */,null /* resuming */, "finish");......}......} finally {mAtmService.continueWindowLayout();}}
    1. 设置当前 Activity 正在 finish (ActivityRecord下的变量finishing为true)
    1. 这里打印了第1个 MainActivity2 的 events 日志:wm_finish_activity
    1. ActivityResults 相关,当前不关心
    1. 设置 Activity 不可见
    1. 这里有2个日志打印,都表示在处理 finish 之前需要先执行 pause(那下一步肯定是触发 pause)
    1. 开始执行 MainActivity2 的 pause

2.1 makeFinishingLocked

# ActivityRecordvoid makeFinishingLocked() {if (finishing) {return;}// 表示当前Activity正在执行finish流程finishing = true;......}

这里 finishing 还是挺重要的, 后面判断是否要 destory 这个 Activity 就会用到这个变量。

2.2 开始执行pause流程 – wm_pause_activity

pause 流程之前已经看过了,这边直接执行了 TaskFragment::startPausing ,那下一步肯定就是直接调用 TaskFragment::schedulePauseActivity 然后构建执行 PauseActivityItem 了

# TaskFragmentvoid schedulePauseActivity(ActivityRecord prev, boolean userLeaving,boolean pauseImmediately, String reason) {// Proto日志ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);try {// 重点* 1. 输出events 日志 wm_pause_activityEventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),prev.shortComponentName, "userLeaving=" + userLeaving, reason);// 重点* 2. 构建并执行PauseActivityItemmAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),prev.token, PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately));} catch (Exception e) {......}}
    1. 打印第2个 MainActivity2 的events 日志: wm_pause_activity
    1. 构建执行 PauseActivityItem 事务

2.3 addToStopping 流程 – wm_add_to_stopping

下一步的 events 日志为:wm_add_to_stopping 也就是 MainActivity2 的 addToStopping 流程。

这个之前在【Activity生命周期之onStop】分析过,可能场景不同逻辑不完全一致,但是核心逻辑都是一样的,就不再跟了,

肯定是将 MainActivity2 添加到 ActivityTaskSupervisor 下的 mStoppingActivities 集合。

再下面的就是 MainActivity 的一些生命周期处理,当前也不是分析重点。

2.4 触发销毁 – wm_destroy_activity

整个 events 日志里没有 wm_stop_activity 而是换成了 wm_destroy_activity ,但是有 wm_on_stop_called 和 wm_on_destroy_called 说明会执行 onStop 和onDestroy。

这个是符合我已知信息的,但是没有 wm_stop_activity 让我有点意外,那说明没有构建 StopActivityItem ,也就是说 onStop 可能是那个在 TransactionExecutorHelper::getLifecyclePath 触发的,这个方法会打log,在日志中确实搜到了这段log:

 D TransactionExecutor: tId:-185591831 Cycle activity: .MainActivity2 from: ON_PAUSE to: ON_DESTROY excludeLastState: true

wm_destroy_activity 日志打印的方法在 ActivityRecord::destroyImmediately 方法,加上堆栈后得到以下信息

04-08 11:18:26.452 12717 12883 E biubiubiu:   destroyImmediately: ActivityRecord{630bcae u0 com.example.myapplication/.MainActivity2} t12 f}}
04-08 11:18:26.452 12717 12883 E biubiubiu: java.lang.Exception
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityRecord.destroyImmediately(ActivityRecord.java:3732)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityRecord.destroyIfPossible(ActivityRecord.java:3675)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor.processStoppingAndFinishingActivities(ActivityTaskSupervisor.java:1959)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor.activityIdleInternal(ActivityTaskSupervisor.java:1403)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor$ActivityTaskSupervisorHandler.activityIdleFromMessage(ActivityTaskSupervisor.java:2426)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor$ActivityTaskSupervisorHandler.handleMessageInner(ActivityTaskSupervisor.java:2459)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor$ActivityTaskSupervisorHandler.handleMessage(ActivityTaskSupervisor.java:2401)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at android.os.Handler.dispatchMessage(Handler.java:106)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at android.os.Looper.loopOnce(Looper.java:210)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at android.os.Looper.loop(Looper.java:297)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at android.os.HandlerThread.run(HandlerThread.java:67)
04-08 11:18:26.452 12717 12883 E biubiubiu: 	at com.android.server.ServiceThread.run(ServiceThread.java:44)

可以看到是熟悉的 ActivityTaskSupervisor::processStoppingAndFinishingActivities 方法触发的。
看一下代码:

# ActivityTaskSupervisor/*** Processes the activities to be stopped or destroyed. This should be called when the resumed* 处理要停止或销毁的Activity。这应该在resumed时调用* activities are idle or drawn.*/private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,boolean processPausingActivities, String reason) {// 准备要执行 Stop 的Activity 集合 ArrayList<ActivityRecord> readyToStopActivities = null;// 重点 * 1. 遍历mStoppingActivitiesfor (int i = mStoppingActivities.size() - 1; i >= 0; --i) {// 获取到ActivityRecord (当前分析场景就1个)final ActivityRecord s = mStoppingActivities.get(i);final boolean animating = s.isAnimating(TRANSITION | PARENTS,ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)|| s.inTransition();// 日志ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "+ "finishing=%s", s, s.nowVisible, animating, s.finishing);// 条件满足才执行if (!animating || mService.mShuttingDown) {......// 打印 准备stop的logProtoLog.v(WM_DEBUG_STATES, "Ready to stop: %s", s);if (readyToStopActivities == null) {readyToStopActivities = new ArrayList<>();}// 重点 * 2. 添加进集合readyToStopActivities.add(s);// 从集合中移除mStoppingActivities.remove(i);}}// 重点 * 3. 遍历readyToStopActivitiesfinal int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();for (int i = 0; i < numReadyStops; i++) {final ActivityRecord r = readyToStopActivities.get(i);// 4. 检查该ActivityRecord对象是否在历史记录中。  if (r.isInHistory()) {// 如果该ActivityRecord对象正在结束(可能是用户或系统触发的结束操作)。if (r.finishing) {// TODO(b/137329632): Wait for idle of the right activity, not just any.// 重点* 4.1 尝试执行destroy  (finish流程)r.destroyIfPossible(reason);} else {// 重点* 4.2 尝试停止它 (stop流程)r.stopIfPossible();}}}......}

这个方法在分析 stop 流程已经看过了,区别在于最后执行的逻辑不同,因为在前面介绍 SystemService 端处理finish 时,开始就会执行 ActivityRecord::finishIfPossible ,然后触发
ActivityRecord::makeFinishingLocked 将 finishing 设置为true, 表示这个Activity 正在执行 finish 流程,所以在执行到这个方法时,就会直接走 ActivityRecord::destroyIfPossible

这里就是finish 和 stop 的区别

# ActivityRecordboolean destroyIfPossible(String reason) {// 1. 设置状态为 FINISHINGsetState(FINISHING, "destroyIfPossible");// Make sure the record is cleaned out of other places.// 2. 从需要stop的集合中移除mTaskSupervisor.mStoppingActivities.remove(this);......// 3. 设置 finishing = truemakeFinishingLocked();// 4. 尝试立即销毁(正常返回false)final boolean activityRemoved = destroyImmediately("finish-imm:" + reason);......ProtoLog.d(WM_DEBUG_CONTAINERS, "destroyIfPossible: r=%s destroy returned "+ "removed=%s", this, activityRemoved);return activityRemoved;}
    1. 这里将 ActivityRecord 的状态设置为 FINISHING
    1. 从stop列表中移除
    1. 设置 finishing = true (不过当前场景 finishing 已经为true了)
    1. 执行 destroyImmediately 方法处理后续流程
# ActivityRecordboolean destroyImmediately(String reason) {......// 如果状态正在处于 destory 则returnif (isState(DESTROYING, DESTROYED)) {ProtoLog.v(WM_DEBUG_STATES, "activity %s already destroying, skipping "+ "request with reason:%s", this, reason);return false;}// events 日志:wm_destroy_activity  EventLogTags.writeWmDestroyActivity(mUserId, System.identityHashCode(this),task.mTaskId, shortComponentName, reason);......// logif (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);// 构建执行 Destroy 事务mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,DestroyActivityItem.obtain(finishing, configChangeFlags));......}
    1. 打印了 events 日志:wm_destroy_activity
    1. 构建执行DestroyActivityItem (注意这里第一个参数,当前场景为true)

那后续的逻辑就又到应用端处理了,服务端整理的堆栈如下:

ActivityTaskSupervisorHandler::handleMessageActivityTaskSupervisorHandler::handleMessageInner   -- IDLE_NOW_MSGActivityTaskSupervisor$ActivityTaskSupervisorHandler::activityIdleFromMessageActivityTaskSupervisor::activityIdleInternalActivityTaskSupervisor::processStoppingAndFinishingActivitiesActivityRecord::destroyIfPossibleActivityRecord::destroyImmediatelyActivityRecord::setState  FINISHINGActivityRecord::makeFinishingLockedActivityRecord::destroyImmediately 构建执行 DestroyActivityItem 

3. 应用端处理处理finish – wm_on_stop_called, wm_on_destroy_called

DestroyActivityItem 的定义如下:

# DestroyActivityItemprivate boolean mFinished;@Overridepublic void execute(ClientTransactionHandler client, ActivityClientRecord r,PendingTransactionActions pendingActions) {Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");// 执行下一步,第二个参数为mFinishedclient.handleDestroyActivity(r, mFinished, mConfigChanges,false /* getNonConfigInstance */, "DestroyActivityItem");Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}@Overridepublic int getTargetState() {return ON_DESTROY;}public static DestroyActivityItem obtain(boolean finished, int configChanges) {DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class);if (instance == null) {instance = new DestroyActivityItem();}// 复制给mFinishedinstance.mFinished = finished;instance.mConfigChanges = configChanges;return instance;}

3.1 onStop – wm_on_stop_called

前面说过这个流程的onStop 是通过 TransactionExecutorHelper::getLifecyclePath 方法触发的,执行时机在触发 DestroyActivityItem::execute 之前。
如果对这点有疑问的参考 【Activity生命周期之onCreate,onStart,onResume】的Activity 生命周期事务跨进程处理方式详解
onStop 流程也在 【onStop】讲过了。

最终会输出 events日志:wm_on_stop_called 并执行 Activity 的onStop

当然日志的打印顺序能更直观的证明

在这里插入图片描述

3.2 onDestroy – wm_on_destroy_called

根据 DestroyActivityItem::execute 的定义直接看 ActivityThread::handleDestroyActivity 方法

# ActivityThread@Overridepublic void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,boolean getNonConfigInstance, String reason) {// 1. 主流程 onDestroyperformDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);// 2. 视图相关处理cleanUpPendingRemoveWindows(r, finishing);WindowManager wm = r.activity.getWindowManager();View v = r.activity.mDecor;if (v != null) {if (r.activity.mVisibleFromServer) {mNumVisibleActivities--;}IBinder wtoken = v.getWindowToken();if (r.activity.mWindowAdded) {if (r.mPreserveWindow) {// ......需要保留Window} else {......// 立即移除当前视图 (触发WindowManagerGlobal移除)wm.removeViewImmediate(v);}}if (wtoken != null && r.mPendingRemoveWindow == null) {// 后续会触发 ViewRootImpl::doDie 移除ViewWindowManagerGlobal.getInstance().closeAll(wtoken,r.activity.getClass().getName(), "Activity");} else if (r.mPendingRemoveWindow != null) {......}r.activity.mDecor = null;}......if (finishing) {// 3. 通知 SystemService 端ActivityClient.getInstance().activityDestroyed(r.token);}mSomeActivitiesChanged = true;}

做了三件事:

    1. 执行到 Activity 的 onDestroy
    1. 处理UI相关的移除
    1. 通知 SystemService 端这个 Activity 已经 Destroy 了,后续该干啥干啥

3.2.1 onDestroy 流程

# ActivityThreadvoid performDestroyActivity(ActivityClientRecord r, boolean finishing,int configChanges, boolean getNonConfigInstance, String reason) {Class<? extends Activity> activityClass = null;// 日志if (localLOGV) Slog.v(TAG, "Performing finish of " + r);// 拿到 ActivityactivityClass = r.activity.getClass();r.activity.mConfigChangeFlags |= configChanges;//  当前场景finishing 为trueif (finishing) {r.activity.mFinished = true;}// 如果需要则执行pause逻辑performPauseActivityIfNeeded(r, "destroy");// 如果还没stop, 则需要执行stop逻辑if (!r.stopped) {callActivityOnStop(r, false /* saveState */, "destroy");}......try {r.activity.mCalled = false;// * 主流程mInstrumentation.callActivityOnDestroy(r.activity);......} ......// 设置ActivityClientRecord状态-- ON_DESTROYr.setState(ON_DESTROY);// 发送消息 PURGE_RESOURCESschedulePurgeIdler();......}

个人感觉比较重要的地方都加了注释,然后继续看主流程

# Instrumentationpublic void callActivityOnDestroy(Activity activity) {activity.performDestroy();}
# Activityfinal void performDestroy() {if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performDestroy:"+ mComponent.getClassName());}dispatchActivityPreDestroyed();mDestroyed = true;// 设置Window 下 mDestroyed 为truemWindow.destroy();mFragments.dispatchDestroy();// 重点* 执行onDestroyonDestroy();// wm_on_destroy_calledEventLogTags.writeWmOnDestroyCalled(mIdent, getComponentName().getClassName(),"performDestroy");......Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);}

流程结束!

3.2.2 UI处理

相关的代码都在注释里加上了,创建 Activity 的时候也有一些 UI 的处理,当前 finish 自然也有对应的处理,不过不是当前主要分析的逻辑。
后续会出一篇专门介绍 创建和销毁 Activity 时 UI 的一些处理。

3.3 总结 – 调用链整理

应用端处理的调用链如下:

ActivityThread::handleDestroyActivityActivityThread::performDestroyActivity       -- onDestroy流程Instrumentation::callActivityOnDestroyActivity::performDestroyActivity::onDestroyWindowManagerImpl::removeViewImmediate        -- UI处理WindowManagerGlobal::removeViewActivityClient::activityDestroyed            -- SystemService 进程处理

4 SystemService 进程处理 – activityDestroyed

应用端已经执行完 Destroy 流程了,自然也要通知到 SystemService 进程做对应的后续处理 。

# ActivityClientpublic void activityDestroyed(IBinder token) {try {getActivityClientController().activityDestroyed(token);} catch (RemoteException e) {e.rethrowFromSystemServer();}}
# ActivityClientController@Overridepublic void activityDestroyed(IBinder token) {if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);final long origId = Binder.clearCallingIdentity();synchronized (mGlobalLock) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");try {final ActivityRecord r = ActivityRecord.forTokenLocked(token);if (r != null) {// 重点* 执行 destroyedr.destroyed("activityDestroyed");}} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);Binder.restoreCallingIdentity(origId);}}}

这里有一些log和加了Trace, 但是重点就是执行了 ActivityRecord::destroyed 方法

# ActivityRecord/*** 此方法仅应在Activity被销毁且完成时,由客户端通过Binder调用*/void destroyed(String reason) {// 移除销毁超时定时器removeDestroyTimeout();// 日志ProtoLog.d(WM_DEBUG_CONTAINERS, "activityDestroyedLocked: r=%s", this);// 检查状态if (!isState(DESTROYING, DESTROYED)) {// 不符合预期,抛出异常throw new IllegalStateException("Reported destroyed for activity that is not destroying: r=" + this);}// 如果Activity位于RootTaskif (isInRootTaskLocked()) {// 清理服务cleanUp(true /* cleanServices */, false /* setState */);// 从历史记录中移除该Activity,并传递销毁原因removeFromHistory(reason);}// 重点* 通知根窗口容器恢复焦点任务的顶层ActivitymRootWindowContainer.resumeFocusedTasksTopActivities();}

重点就是在最后执行了 RootWindowContainer::resumeFocusedTasksTopActivities .
因为已经有一个 Activity 执行完了finish 流程,那界面上面显示的 Activity 当然会有一些变化,所以执行一些这个流程,来确保屏幕内容的正常显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/438525.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python爬虫 - 初识爬虫

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、爬虫的关键概念 &#xff08;一&#xff09;HTTP请求与响应 &#xff0…

Discord:报错:A fatal Javascript error occured(解决办法)

按 Windows 键 R 并输入 %appdata% 选择 discord 文件夹并将其删除。 再次按 Windows 键 R 并输入 %LocalAppData% 选择 discord 文件夹并再次将其删除。 附加&#xff1a; 如果还不行&#xff0c;就通过官网下载吧&#xff0c;这个问题通过epic下载可能会有

TypeScript 第三部分 扩展

1. 声明文件 主要作用&#xff1a; 类型声明&#xff1a;为库或模块提供类型信息。全局声明&#xff1a;为全局作用域中的类型和变量提供声明。类型兼容性&#xff1a;确保第三方库或自定义代码的类型正确性。代码提示与检查&#xff1a;在开发环境中提供更好的代码提示和类型…

关于深度学习torch的环境配置问题

已经下好了torch在虚拟环境中&#xff0c;结果在ipynb文件中无法运行 后来在终端直接用python语句编译 发现没有问题 在编辑测试py文件 发现runcode有问题 原来是插件默认base环境 具体操作参考VS Code插件Code Runner使用python虚拟环境_coderunner怎么在虚拟环境中使用-CSD…

11. 异步编程

计算机的核心部分&#xff0c;即执行构成我们程序的各个步骤的部分&#xff0c;称为处理器。我们迄今为止看到的程序都会让处理器忙个不停&#xff0c;直到它们完成工作。像操作数字的循环这样的程序的执行速度几乎完全取决于计算机处理器和内存的速度。但是&#xff0c;许多程…

微服务nginx解析部署使用全流程

目录 1、nginx介绍 1、简介 2、反向代理 3、负载均衡 2、安装nginx 1、下载nginx 2、解压nginx安装包 3、安装nginx​编辑 1、执行configure命令 2、执行make命令 4、启动nginx 1、查找nginx位置并启动 2、常用命令 3、反向代理 1、介绍反向代理配置 1、基础配置…

Windows上面搭建Flutter Android运行环境

Windows 上面搭建 Flutter Android 运 行环境&#xff08;适用于 Flutter3.7.3之后的版本&#xff09;。 提示&#xff1a; Flutter3.7.x 之前的版本配置请参考教程&#xff0c;或者参考文档《 Flutter Android 开发环境搭建&#xff0c;适用于 Flutter3.3.10之前的版本》。 …

MySQL-SQL(DDL、DML、DQL、DCL)

一、 MySQL启动&#xff1a; MySQL客户端连接&#xff1a; 二、SQL &#xff11;.SQL通用语法 (1) SQL语句可以单行或多行书写&#xff0c;以分号结尾。 (2) SQL语句可以使用空格/缩进来增强语句的可读性。 (3) MySQL数据库的SQL语句不区分大小写&#xff0c;关键字建议使…

宣城自闭症咨询寄宿学校:为家庭提供专业支持

宣城与广州的自闭症教育探索&#xff1a;星贝育园为家庭提供专业支持 在自闭症儿童教育的广阔领域中&#xff0c;寄宿学校作为一种特殊而重要的教育模式&#xff0c;正逐渐展现出其独特的价值和意义。虽然本文标题提及了宣城的自闭症咨询寄宿学校&#xff0c;但我们的焦点将转…

UE4_Niagara基础实例—4、静态网格体表面生成粒子

效果图&#xff1a; 分析&#xff1a;在物体面上生成粒子&#xff0c;改变粒子的不透明度&#xff0c;让粒子收到力&#xff0c;并添加紊乱&#xff0c;类似于水蒸气。 操作步骤&#xff1a; 1、创建个niagara 系统&#xff0c;使用模版 simple sprite burst。简单调节参数。…

【算法】链表:160.相交链表(easy)+双指针

系列专栏 《分治》 《模拟》 《Linux》 目录 1、题目链接 2、题目介绍 3、解法&#xff08;双指针&#xff09; 返回结果 算法正确性 时间复杂度 4、代码 1、题目链接 160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 2、题目介绍 ​ 3、解法&#xff08;…

[C#]C# winform部署yolov11目标检测的onnx模型

yolov11官方框架&#xff1a;https://github.com/ultralytics/ultralytics 【测试环境】 vs2019 netframework4.7.2 opencvsharp4.8.0 onnxruntime1.16.2 【效果展示】 【实现部分代码】 using System; using System.Collections.Generic; using System.ComponentModel;…

安卓真机调试“no target device found“以及“ INSTALL_FAILED_USER_RESTRICTED“两个问题的解决办法

目录 1 no target device found问题解决办法 2 “INSTALL_FAILED_USER_RESTRICTED”解决办法 使用android studio 2023.2.1.23windows版本。手机为小米K70 Pro 1 no target device found问题解决办法 参考小米手机如何开启usb调试功能&#xff1f; (baidu.com) 1 联接手机…

Pikachu-File Inclusion-远程文件包含

远程文件包含漏洞 是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的&#xff0c;因此漏洞一旦存在&#xff0c;危害性会很大。但远程文件包含漏洞的利用条件较为苛刻&#xff1b;因此&#xff0c;在web应用系统的功能设计上尽量不要让前端用户直接传变…

Pikachu-Sql-Inject -基于boolian的盲注

基于boolean的盲注: 1、没有报错信息显示&#xff1b; 2、不管是正确的输入&#xff0c;还是错误的输入&#xff0c;都只显示两种情况&#xff0c;true or false&#xff1b; 3、在正确的输入下&#xff0c;输入and 1 1/and 1 2发现可以判断&#xff1b; 布尔盲注常用函数&…

【论文笔记】Visual Instruction Tuning

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Visual Instruction Tunin…

关于 JVM 个人 NOTE

目录 1、JVM 的体系结构 2、双亲委派机制 3、堆内存调优 4、关于GC垃圾回收机制 4.1 GC中的复制算法 4.2 GC中的标记清除算法 1、JVM 的体系结构 "堆"中存在垃圾而"栈"中不存在垃圾的原因: 堆(Heap) 用途:堆主要用于存储对象实例和数组。在Java中…

微服务_3.微服务保护

文章目录 一、微服务雪崩及解决方法1.1、超时处理1.2、仓壁模式1.3、断路器1.4、限流 二、Sentinel2.1、流量控制2.1.1、普通限流2.1.2、热点参数限流 2.2、线程隔离 一、微服务雪崩及解决方法 微服务中&#xff0c;服务间调用关系错综复杂&#xff0c;一个微服务往往依赖于多个…

关于CSS 案例_新闻内容展示

新闻要求 标题:居中加粗发布日期: 右对齐分割线: 提示, 可以使用 hr 标签正文/段落: 左侧缩进插图: 居中显示 展示效果 审核过不了&#xff0c;内容没填大家将就着看吧。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&qu…

安卓13设置删除网络和互联网选项 android13隐藏设置删除网络和互联网选项

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改4.1修改方法14.2修改方法25.编译6.彩蛋1.前言 有些客户不想让用户修改默认的网络配置,禁止用户进入里面调整网络相关的配置。 2.问题分析 像这个问题,我们有好几种方法去处理,这种需求一般…