主頁 > 軟體設計 > Android四大組件之Activity啟動流程原始碼實作詳解(四)

Android四大組件之Activity啟動流程原始碼實作詳解(四)

2020-10-16 22:03:21 軟體設計

???? Pause前臺顯示Activity,Resume目標Activity

Android四大組件原始碼實作詳解系列博客目錄:

Android應用行程創建流程大揭秘
Android四大組件之bindService原始碼實作詳解
Android四大組件之Activity啟動流程原始碼實作詳解概要
Android四大組件之Activity啟動流程原始碼實作詳解(一)
Android四大組件之Activity啟動流程原始碼實作詳解(二)
Activity啟動流程(三)- Activity Task調度演算法復盤分析
Activity啟動流程(四)- Pause前臺顯示Activity,Resume目標Activity


前言

??還記得我們在前面博客Android四大組件之Activity啟動流程原始碼實作詳解(二)Activity啟動流程(三)- Activity Task調度演算法復盤分析中做的艱苦卓越的斗爭嗎!這場戰役之慘烈,戰況之持久前所未有!雖然程序是疼苦的,但是戰果也是顯赫和令人滿意的,通過上述戰役我們取得了如下的階段性成果:

  • 初始化了Activity啟動狀態
  • 計算了啟動launchFlag
  • 計算了呼叫者的ActivityStack
  • 檢查了是否存在復用的TaskRecord
  • 對于存在復用的TaskRecord則進行相應的ActivityStack、TaskRecord的移動(說實話,我也沒有真的搞懂,希望這塊比較有經驗的小伙們能和我一起學習)
  • 計算了當前啟動Activity所屬的TaskRecord
  • 把當前啟動的Activity放到所屬TaskRecord的堆疊頂
  • 并且前面的TaskRecord放到了ActivityStack的堆疊頂

總而言之經過上述一頓猛虎般的操作,此時要啟動的目標Actvity及其對應的task位置以及ActivityStack已經安排妥當,現在可以準備接下來的相關作業了,那么在本篇博客中我們將繼續分析system_server對Activity啟動請求的處理流程:

  • system_server行程通過AMS處理啟動Activity請求
    5.Pause前臺Activity
    6.Resume請求的目標Activity
    7.AMS請求zygote行程為目標Activity創建所屬行程

注意:本篇的介紹是基于Android 7.xx平臺為基礎的,其中涉及的代碼路徑如下:

frameworks/base/services/core/java/com/android/server/am/
  --- ActivityManagerService.java
  --- ProcessRecord.java
  --- ActivityRecord.java
  --- ActivityResult.java
  --- ActivityStack.java
  --- ActivityStackSupervisor.java
  --- ActivityStarter.java
  --- TaskRecord.java 

frameworks/base/services/core/java/com/android/server/pm/
 --- PackageManagerService.java
 
frameworks/base/core/java/android/content/pm/
--- ActivityInfo.java

frameworks/base/core/java/android/app/
  --- IActivityManager.java
  --- ActivityManagerNative.java (內部包含AMP)
  --- ActivityManager.java
  --- AppGlobals.java
  --- Activity.java
  --- ActivityThread.java(內含AT)
  --- LoadedApk.java  
  --- AppGlobals.java
  --- Application.java
  --- Instrumentation.java
  
  --- IApplicationThread.java
  --- ApplicationThreadNative.java (內部包含ATP)
  --- ActivityThread.java (內含ApplicationThread)
  --- ContextImpl.java

并且在后續的原始碼分析程序中為了簡述方便,會將做如下簡述:

  • ApplicationThreadProxy簡稱為ATP
  • ActivityManagerProxy簡稱為AMP
  • ActivityManagerService簡稱為AMS
  • ActivityManagerNative簡稱AMN
  • ApplicationThreadNative簡稱ATN
  • PackageManagerService簡稱為PKMS
  • ApplicationThread簡稱為AT
  • ActivityStarter簡稱為AS,這里不要和ActivityServices搞混淆了
  • ActivityStackSupervisor簡稱為ASS

在正式開始今天博客相關原始碼分析前,還是先奉上呼叫的時序圖以便小伙們先從整體上有個清晰的概括,然后再從細節開擼!

在這里插入圖片描述



一.Pause處于Resume狀態Actvity以及Resume目標Activity

??戰斗還沒有結束,革命尚未成功仍需努力!讓我們帶著前面博客Android四大組件之Activity啟動流程原始碼實作詳解(二)未完成使命繼續前進!

//[ActivityStarter.java]
	/*	這里的sourceRecord是指發起呼叫者
		r是指本次的將要啟動的Activity,startFlags取值為0,
		doResume的值為true
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {
         ...
        if (mDoResume) {
            if (!mLaunchTaskBehind) {
			/*
			  * 設定當前focused,因為經過以上幾步,啟動的activity已經轉移到
			  *堆疊頂端,這時候設定AMS當前focused的Activity
			  *另外呼叫這個函式也會有ActivityStack、Task堆疊的移動,即呼叫各自堆疊把當
			  *前正在啟動的Activity所屬的Task、ActivityStack移動到堆疊頂
			  */
                mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
            }
            final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
            if (!mTargetStack.isFocusable()//當前的目標Stack被設定成了焦點所以不會走此分支
                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                    && mStartActivity != topTaskActivity)) {
                    ...
            } else {
            	//開始resume,詳見章節1.1
                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);
            }
        } else {
            mTargetStack.addRecentActivityLocked(mStartActivity);
        }

        return START_SUCCESS;
        ...
}

搞了這么久,發起端Activity和目標Actvity的生命周期一個都沒有看到,小伙們是不是心里有些慌了! 不急,這不它就來了,讓我們先來看看Acitivyt A啟動Activty B的正常生命周期調度:

---> Activity A onCreate
---> Activity A onStart 
---> Activity A onResume
---> Activity A onPause
---> Activity B onCreate
---> Activity B onStart
---> Activity B onResume
---> Activty  A onStop

而我們接下來會沿著上面的主線進行相關的分析,這里我們從A的onPause為起始段開始本篇博客的分析,此時我們將上述場景Activity A啟動Activity B帶入下面的相關分析中,


1.1 ASS.resumeFocusedStackTopActivityLocked

??讓我們對ActivityStackSupervisor中的resumeFocusedStackTopActivityLocked繼續分析,其中Android中對于Activity的啟動主要表現在如下兩個方面:

  • 第一:AMS對于Activity比較復雜的部分即為Task任務堆疊和Stack的處理,這部分詳見Activity啟動流程(三)- Activity Task調度演算法復盤分析
  • 第二:本章節要重點介紹的resumed相關操作,

并且關于這兩部分讀者最好參考我前面的博客先假定某種啟動模式,然后進行相關的分析,否則很容易陷入代碼中而不能自拔,而我們這里的resumeFocusedStackTopActivityLocked其實對應的就是Activity的生命周期,

//[ActivityStackSupervisor.java]
    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, 
            ActivityRecord target, 
            ActivityOptions targetOptions) {
        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);//詳見章節1.1.1
        }
		...
    }

讓我們單刀直入直搗黃龍,接著分析ActivityStack的resumeTopActivityUncheckedLocked方法(不是我不分析,確實是因為沒有啥好分析的),

1.1.1 ActivityStack.resumeTopActivityUncheckedLocked

//[ActivityStack.java]
    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mStackSupervisor.inResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
			...
            result = resumeTopActivityInnerLocked(prev, options);//詳見章節1.2
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }

這里沒有過多可以介紹的,讓我們單刀直入直搗黃龍,接著分析其方法resumeTopActivityInnerLocked,在分析這個方法之前小伙們該上廁所的可以上廁所了,喝水的可以喝水了,因為這又是一個重量級的方法,


1.2 ActivityStack.resumeTopActivityInnerLocked

??在正式開始分析該方法之前,我們先來看看該方法的入參,方法入參的名字挺奇怪的!

引數型別引數名稱引數功能
ActivityRecordprev目標Activity的相關資訊
ActivityOptionsoptions額外附加資訊

不知各位小伙們發現沒有,在上一個方法中發起呼叫時,傳入的引數名稱是target,表示待啟動的ActivityRecord,但這里搖身一變,引數名稱卻是prev,看引數意思是表示之前啟動的ActivityRecord,即將要進入Pausing狀態的那個Activity,到底意欲幾何,是不是谷歌的哥哥搞錯了啊?這里小伙們先暫且將該疑問放在一邊,待我們后續的原始碼中一一揭秘,此時我們要重點關注,此時的入參為位目標ActivityRecord,雖然它換了一個馬甲叫做prev,

//[ActivityStack.java]
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {


        if (!mService.mBooting && !mService.mBooted) {
        	// 如果系統還未啟動完畢,那AMS還不能正常作業,所以也不能顯示Activity,主要是為防止沒有開機啟動完成
            return false;
        }
		//此處忽略
        ActivityRecord parent = mActivityContainer.mParentActivity;
		...

		// 當前AS中可能存在一些正處于Intializing狀態的ActivityRecord,
	    // 如果這些ActivityRecord不是位于堆疊頂,而且正在執行視窗啟動影片,
	    // 那么,就需要取消這些Activity的啟動影片,
        mStackSupervisor.cancelInitializingActivities();


        /*
	        找到第一個沒有finishing的堆疊頂activity,通常指向了要啟動的Activity目標組件
	        此場景下prev和next都是同一個,都指向了Activity B
        */
        final ActivityRecord next = topRunningActivityLocked();


        //這個變數是表示是否回呼Activity中的onUserLeaveHint和onUserInteraction函式
        final boolean userLeaving = mStackSupervisor.mUserLeaving;
        mStackSupervisor.mUserLeaving = false;

        final TaskRecord prevTask = prev != null ? prev.task : null;
        if (next == null) {//這個表示如果當前ActivityStack不存在待啟動的Activity,那么會啟動Launcher桌面
            final String reason = "noMoreActivities";
            final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack()
                    ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();

			// 當前AS不是全屏顯示,則需要將焦點切換到下一個待顯示的AS
            if (!mFullscreen && adjustFocusToNextFocusableStackLocked(returnTaskType, reason)) {
                return mStackSupervisor.resumeFocusedStackTopActivityLocked(
                        mStackSupervisor.getFocusedStack(), prev, null);
            }

            
            ActivityOptions.abort(options);

			// 默認情況下,Stack都是占據全屏的,所以,當前Stack如果沒有要顯示的Activity,則會要求顯示桌面
            return isOnHomeDisplay() &&
                    mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
        }

        next.delayedResume = false;


        //檢查要啟動的Activity 組件是否等于當前被激活的 Activity 組件,如果等于
        //并且處于 RESUMED 狀態,直接回傳,我們前面演示的啟動情況很顯然不滿足條件
        if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                    mStackSupervisor.allResumedActivitiesComplete()) {
             //當前正在顯示的Activity正好就是下一個待顯示的Activity,
            // 那么,就中斷對目標ActivityRecord的調度
            mWindowManager.executeAppTransition();
            mNoAnimActivities.clear();
            ActivityOptions.abort(options);
            return false;
        }

        final TaskRecord nextTask = next.task;
         /*這個是對上一個resumed的Activity的相關處理
		 * 由于我們是第一次啟動B Activity,所以不可能處于finish跳過此處
		 */
        if (prevTask != null && prevTask.stack == this &&
                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
			...
        }

        // 系統進入休眠狀態,當前Stack的堆疊頂Activity已經處于Paused狀態
        // 那么,中斷待顯示Activity的相關調度(有點拗口,學習原始碼就是這么枯燥的事情)
        if (mService.isSleepingOrShuttingDownLocked()
                && mLastPausedActivity == next
                && mStackSupervisor.allPausedActivitiesComplete()) {
            mWindowManager.executeAppTransition();
            mNoAnimActivities.clear();
            ActivityOptions.abort(options);
            return false;
        }

		...

		/*
			在ASS中存在很多的資料結構,用來統一管理ActivityRecord的狀態
	    	譬如mStoppingActivities記錄了當前所有處于Stopping狀態的ActivityRecord
	    	mGoingToSleepActivities記錄了當前所有要進入休眠狀態的ActivityRecord
	    	在某些場景下,待顯示的ActivityRecord可能處于這些陣列中,但需要從中剔除
		*/
        mStackSupervisor.mStoppingActivities.remove(next);
        mStackSupervisor.mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mStackSupervisor.mWaitingVisibleActivities.remove(next);

        

         // 如果當前ASS中還有ActivityRecord不是處于PAUSED, STOPPED或STOPPING這三個狀態之一,
    	// 那么,需要先等這些ActivityRecord進入停止狀態
        if (!mStackSupervisor.allPausedActivitiesComplete()) {
            return false;
        }

??分析至此,讓我們先緩緩,停下前進的腳步看看我們來時的路!前面的代碼片段,主要是做一些初始化和可能的"例外"處理作業,雖然我們待顯示的目標ActivityRecord已經位于堆疊頂,但要真正將其顯示到前臺來,即執行目標Activity的onCreate/onStart/onResume等狀態,這一路有很多障礙和初始化作業還處理,或者說還有很多前提條件需要滿足,譬如,系統要休眠時,當前啟動目標Activity程序要中斷;當前ASS中有Activity正處于Pausing狀態時,也需要等相關Activity執行完畢才行,我們可以將上述的相關作業認為是準備階段!前路漫漫是征途,我將上下而求索!

//[ActivityStack.java]
		/*
			setLaunchSource設定待啟動的Activity的資訊
			跟進setLaunchSource原始碼發現它最侄訓獲取一個WakeLock,保證在顯示Activity的程序中,系統不會進行休眠狀態
		*/
        mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
        

         /*
           目標Activity的啟動引數中是否包含FLAG_RESUME_WHILE_PAUSING
   		   如果存在FLAG_RESUME_WHILE_PAUSING的flag,表示可以在當前顯示的發起端Activity執行Pausing時,
   		   能同時進行Resume操作
    	   即變數dontWaitForPause的取意就是不需要等到Activity執行Pause完畢
    	*/
        final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;


		/* 
		  這個是pause掉不是FocusedStack的其它ActivityStack的堆疊頂activity
 		  對于不是當前focusStack的并且存在有mResumedActivity不為null的都要paused
 		  譬如從Luncher啟動一個新的App時會走入此分支
 		*/
		boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
		/********************************************************************************/
			//這里是為了演示使用
			//ASS.java
		    boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
		        boolean someActivityPaused = false;
		        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
		            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
		            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
		                final ActivityStack stack = stacks.get(stackNdx);
		                if (!isFocusedStack(stack) && stack.mResumedActivity != null) {
		                    someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
		                            dontWait);
		                }
		            }
		        }
		        return someActivityPaused;
		    }			
		/********************************************************************************/

		//此時要帶入真是場景了,此時的mResumedActivity表示目標Stack堆疊中處于Resume狀態的Activity,那么在此場景下就是Activity A,這個因該比較容易理解
		if (mResumedActivity != null) {
			//當前resumd狀態activity不為空,則需要先暫停該Activity
			// pause當前堆疊的activity,即執行Activity的生命周期onPause
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);//詳見章節1.3
			
        }
        if (pausing) {//當前有正在pause的Activity,尼瑪按照我們場景Activity A啟動Activity B,那不是到此就結束了啊,直接回傳了,事實就是這樣的,尼瑪是不是走錯片場了,后續你就知道了
            if (next.app != null && next.app.thread != null) {
                mService.updateLruProcessLocked(next.app, true, null);
            }
            return true;
        }
         //檢查要啟動的Activity 組件是否等于當前被激活的 Activity 組件,如果等于
        //并且處于 RESUMED 狀態,直接回傳,我們前面演示的啟動情況很顯然不滿足條件 
        else if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                mStackSupervisor.allResumedActivitiesComplete()) {
            mWindowManager.executeAppTransition();
            mNoAnimActivities.clear();
            ActivityOptions.abort(options);
            return true;
        }

??我們在此就此打住,先不往后分析resumeTopActivityInnerLocked的相關邏輯了,實時上啟動目標Acitivity第一次進入該方法時也會從此處回傳,事實就是這樣的,尼瑪是不是走錯片場了,后續你就知道了,讓我們接著后續分析前面章節1.2提出的問題也會解決了,至于此處為啥在此就return也會解決了,


1.3 Pause前臺顯示的Activity

??我們對要顯示的目標Activity已經做好入堆疊作業了,就是放在Stack的堆疊頂,我們可以通過方法ActivityStack.topRunningActivityLocked可以找到它,然后如果當前要Resume的目標Activity不是之前已經Resume的Activity,那么必須將Pause之前的Activity才行,而這部分的具體作業分兩部分完成:

  • Pause其他所有已經focus的任務堆疊的mResumedActivity,呼叫方法ASS.pauseBackStacks執行
  • Pause當前任務堆疊的mResumedActivity

最后上述兩步都會調到核心方法,ActivityStack.startPausingLocked,而這也是我們本章節將要介紹的:

//[ActivityStack.java]
    final boolean startPausingLocked(boolean userLeaving, 
    								boolean uiSleeping, //此時傳遞進來的引數為false
    								boolean resuming,//此時傳遞進來的引數為true
            						boolean dontWait) 
   {
        //判斷當前的Stack堆疊中是否存在正在pausing的Activity
        if (mPausingActivity != null) {
            if (!mService.isSleepingLocked()) {
                completePauseLocked(false);
            }
        }
        
        //獲取當前Stack堆疊中處于Resume狀態的Activity,在我們當前的環境下就是Activity A了
        ActivityRecord prev = mResumedActivity;
        if (prev == null) {
            if (!resuming) {
                mStackSupervisor.resumeFocusedStackTopActivityLocked();
            }
            return false;
        }

		...
		// 變更ActivityStack中pauseActivity的記錄,此處是重點
        mResumedActivity = null;
        mPausingActivity = prev;
        mLastPausedActivity = prev;
        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
        prev.state = ActivityState.PAUSING;
        prev.task.touchActiveTime();
        clearLaunchTime(prev);
        
		...
		 // 通知APP執行發起端的pause操作
        if (prev.app != null && prev.app.thread != null) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false);
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);//詳見章節1.3.1
            } catch (Exception e) {
                mPausingActivity = null;
                mLastPausedActivity = null;
                mLastNoHistoryActivity = null;
            }
        } else {
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }

		//獲取鎖,防止休眠
        if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
            mStackSupervisor.acquireLaunchWakelock();
        }

        if (mPausingActivity != null) {
            if (!uiSleeping) {
                prev.pauseKeyDispatchingLocked();
            } else if (DEBUG_PAUSE) {
            }

            if (dontWait) {
                completePauseLocked(false);
                return false;

            } else {
            	//這個是經典的ANR埋雷,監控APP是否pause超時,時間只有500ms
                Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
                msg.obj = prev;
                prev.pauseTime = SystemClock.uptimeMillis();
                mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
                return true;
            }

        } else {
			...
        }
    }

在正式開始上述的原始碼分析前,我們先來闡述一個重要的知識點,即在這個呼叫程序中涉及到兩個行程,不妨令發起startActivity的發起行程記為行程Process_A,AMS Service所屬行程記為行程Process_B;那么行程Process_A通過Binder機制(采用IActivityManager介面)向行程Process_B發起請求服務,行程Process_B則通過Binder機制(采用IApplicationThread介面)向行程Process_A發起請求服務,也就是說行程Process_A與行程Process_B能相互間主動發起請求,進而完成行程通信,但是這里有一點需要注意IApplicationThread的Binder物體端并沒有注冊到servicemanager行程中,它是一個依賴于實名Binder的匿名Binder,

這里涉及IApplicationThread很重要,它串聯起了AMS對App行程的生命周期及其其它的控制,那么下面直接把其相關的類圖展示如下:

在這里插入圖片描述

這里的IApplicationThread與IActivityManager的Binder通信原理一樣,ATP作為Binder通信的客戶端,ATN作為Binder通信的服務端,其中ApplicationThread繼承ATN類,覆寫其中的部分方法,


并且在目標Activity A的行程創建的時候會存在如下的流程關系(這里小伙們可以先記住,后面再理解)

在這里插入圖片描述

前面分析了一大堆主要想說明如下問題:

  • 這里的IApplicationThread和IActivityManager類似,是可以實作Binder跨行程通信的
  • 客戶端行程可以通過AMP實作和AMS(system_server行程)的通信
  • AMS(system_server行程)可以通過ATP和客戶端發起行程AT通信

如果小伙們對上述Binder跨行程呼叫不是很清晰的,可以參見系列博客Android Binder框架實作原始碼分析,這里就不過多講述了,總之通過prev.app.thread.schedulePauseActivity實際上呼叫的就是ApplicationThread的schedulePauseActivity方法中去了,其呼叫程序可以使用下面的偽代碼來表示:

ATP.schedulePauseActivity()---> 
BinderProxy.transact() --->
BpBinder.transact()--->
binder驅動傳輸--->
JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.schedulePauseActivity()

1.3.1 AT.schedulePauseActivity處理Pause請求

??通過ATP的努力和我們的Binder框架的協助,我們跨越萬水千山,完成了system_server所在行程到發起端所在目的端行程呼叫程序,讓我們接著分析看看目的端行程是怎么處理schedulePauseActivity的RPC請求的,我好難啊!

//[ActivityThread.java]
    private class ApplicationThread extends ApplicationThreadNative {
        public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            int seq = getLifecycleSeq();
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),
                    configChanges,
                    seq);//詳見1.3.2
        }    	
    }

巧用ActivityThread的主執行緒的Handler發送訊息,這里我們可以總結一下規律,通常AMS通過ATP發送過來的訊息,遵循如下的處理邏輯,如下

scheduleXXX() ---> handleXXX()

1.3.2 H.handleMessage

//ActivityThread.java
private class H extends Handler {
	public void handleMessage(Message msg) {
	    switch (msg.what) {
	        ...
                case PAUSE_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handlePauseActivity((IBinder) args.arg1, false,
                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
                            (args.argi1 & DONT_REPORT) != 0, args.argi3);//詳見章節1.3.3
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
	        ...
	    }
	}
}

1.3.3 AT.handlePauseActivity

//[ActivityThread.java]
    private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport, int seq) {
        //獲取需要
        ActivityClientRecord r = mActivities.get(token);


        if (r != null) {

            if (userLeaving) {
                performUserLeavingActivity(r);
            }

            r.activity.mConfigChangeFlags |= configChanges;
            //執行Activity的onPause操作
            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");//詳見章節1.3.4

  
            if (r.isPreHoneycomb()) {
                QueuedWork.waitToFinish();
            }


            if (!dontReport) {
                try {
                	//通知AMS已經Pause成功了
                    ActivityManagerNative.getDefault().activityPaused(token);//詳見章節1.3.5
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }
            mSomeActivitiesChanged = true;
        }
    }

無需多言,直接進入下一關,打怪升級!

1.3.4 AT.performPauseActivity

    final Bundle performPauseActivity(IBinder token, boolean finished,
            boolean saveState, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        return r != null ? performPauseActivity(r, finished, saveState, reason) : null;
    }

    final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
            boolean saveState, String reason) {
		
		...
        performPauseActivityIfNeeded(r, reason);

		...

        return !r.activity.mFinished && saveState ? r.state : null;
    }

    private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
        if (r.paused) {
            // You are already paused silly...
            return;
        }

        try {
            r.activity.mCalled = false;
            mInstrumentation.callActivityOnPause(r.activity);
            EventLog.writeEvent(LOG_AM_ON_PAUSE_CALLED, UserHandle.myUserId(),
                    r.activity.getComponentName().getClassName(), reason);
            if (!r.activity.mCalled) {
				...
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
			...
        }
        r.paused = true;
    }

??還是原來的配方,還是原來的味道,最終通過Instrumentation類回呼了Activity實體的onPause方法,如下:

//[Instrumentation.java]
    public void callActivityOnPause(Activity activity) {
        activity.performPause();
    }

見證奇跡的時刻要到了,讓我們拭目以待:

//[Activity.java]
    final void performPause() {
        mDoReportFullyDrawn = false;
        mFragments.dispatchPause();
        mCalled = false;
        onPause();//執行Activity的onPause方法
        mResumed = false;
        if (!mCalled && getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.GINGERBREAD) {
            throw new SuperNotCalledException(
                    "Activity " + mComponent.toShortString() +
                    " did not call through to super.onPause()");
        }
        mResumed = false;
    }

??尼瑪,太不容易了,終于回呼到了ASS中處于Resume狀態的Activity的onPause方法,這其中涉及的彎彎繞繞可真多啊,網上有有博主說我們在啟動一個Activity的時候最先被執行的是堆疊頂的Activity的onPause方法,我個人不是很贊同上述說法,應該是目標Activity所屬Stack堆疊存在Resume狀態的Activity時會執行其onPause方法,否則執行的就是其它Stack堆疊中的了,

1.3.5 AMS.activityPaused

??繼續回到章節1.3.3未完成之實名,在將當前顯示的Activity執行onPause之后,在該方法的最后面執行了ActivityManagerNative.getDefault().activityPaused(token);方法,這是應用行程告訴system_server服務行程,當前顯示的Activity已經執行完成onPause方法了,通過前面我們的分析,我們知道這句話最侄訓被ActivityManagerService的activityPaused方法執行了!,其呼叫流程可以使用如下的偽代碼來表述:

AMP.activityPaused(...)---> 
BinderProxy.transact(...) --->
BpBinder.transact(...)--->
binder驅動傳輸--->
JavaBBinder.onTransact(...)--->
AMN.onTransact(..)--->
AMN.activityPaused(...)
//[AMS.java]
    public final void activityPaused(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
        	//獲取已經pause的Activity所屬Stack
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                stack.activityPausedLocked(token, false);
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

在activityPaused內部繼續呼叫ActivityStack的activityPausedLocked方法進行下一步的處理,讓我們接著分析:

    final void activityPausedLocked(IBinder token, boolean timeout) {
        final ActivityRecord r = isInStackLocked(token);
        if (r != null) {
            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
            if (mPausingActivity == r) {
                completePauseLocked(true);//此處傳入的引數為true
                return;
            } else {
					...
            }
        }
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    }

接著繼續分析completePauseLocked,注意此時傳入的引數為true,我們繼續分析:

    private void completePauseLocked(boolean resumeNext) {
        ActivityRecord prev = mPausingActivity;
		...
		        if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            if (!mService.isSleepingOrShuttingDownLocked()) {//會進入此分支,繼續章節1.1.1的邏輯
                //此時的prev為前臺顯示已經pause的Activity
                mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked();
                if (top == null || (prev != null && top != prev)) {
                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                }
            }
        }
    	...
    }

經過了一系列的邏輯之后,又呼叫了resumeFocusedStackTopActivityLocked方法,又回到了章節1.1決議的方法中了,此時的傳入的prev就是我們已經pause的Activity的了,而不是目標Activiyt了,現在章節1.2最后的謎題可以解決了,感覺resumeFocusedStackTopActivityLocked不是這么簡單啊!

1.3.6 Pause前臺顯示的Activity小結

??雖然本人也反對分析原始碼程序中大量堆砌原始碼,但是有時候又不得不為之,因為如果不放上原始碼呼叫邏輯,整個流程下來就不是很清晰了,這個也木有辦法,臣妾也不想啊!好嗎,我們來總結一下Pause前臺顯示的Activity的流程,如果站在行程互動的角度出發,其中Pause前臺顯示的Activity牽涉到兩次的跨行程呼叫:

  • AMS通過ATP通知前臺Activity進行相關的onPause操作

  • 前臺顯示的Activity執行onPause成功之后通過AMP跨行程通知AMS已經成功執行

其中整個Pause前臺顯示的Activity的流程可以使用如下的偽代碼流程來表示,如下:

//AMS端
ActivityStack.startPausingLocked(...)	--->
ATP.schedulePauseActivity(...)	---> 
BinderProxy.transact(...)	--->
BpBinder.transact(...)	--->

binder驅動傳輸	--->

//前臺顯示Activity端
JavaBBinder.onTransact(...)	--->
ATN.onTransact(...)	--->
ATN.schedulePauseActivity(...)	--->
ApplicationThread.schedulePauseActivity(...)	--->
ActivityThread.H.handleMessage(...)	--->
ActivityThread.handlePauseActivity(...)	--->
ActivityThread.performPauseActivity(...)	--->
ActiivtyThread.performPauseActivityIfNeeded(...)  --->
Instrumentation.callActivityOnPause(...)   --->
Activity.performPause(...)   --->
Activity.onPause(...)   --->
AMP.activityPaused(...)---> 
BinderProxy.transact(...) --->
BpBinder.transact(...)--->

binder驅動傳輸--->

//AMS端
JavaBBinder.onTransact(...)--->	
AMN.onTransact(..)--->
AMN.activityPaused(...)	

1.4 Resume目標Activity

??愛的魔力轉圈圈,讓我們繼續第二輪回,重新開始原始碼的分析

//[ActivityStackSupervisor.java]
    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, 
            ActivityRecord target, //此時的target為前臺已經處于pause狀態的Activity,如果在我們當前的場景下即為Activity A
            ActivityOptions targetOptions) {
        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
		...
    }

繼續往下分析此時我想各位應該明白我們在章節1.2的疑問了


//[ActivityStack.java]

    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
		...
        boolean result = false;
        try {
			...
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {


        if (!mService.mBooting && !mService.mBooted) {
        	// 如果系統還未啟動完畢,那AMS還不能正常作業,所以也不能顯示Activity,主要是為防止沒有開機啟動完成
            return false;
        }
		//此處忽略
        ActivityRecord parent = mActivityContainer.mParentActivity;
		...

		// 當前AS中可能存在一些正處于Intializing狀態的ActivityRecord,
	    // 如果這些ActivityRecord不是位于堆疊頂,而且正在執行視窗啟動影片,
	    // 那么,就需要取消這些Activity的啟動影片,
        mStackSupervisor.cancelInitializingActivities();


        /*
	        找到第一個沒有finishing的堆疊頂activity,通常指向了要啟動的Activity目標組件
	        此場景下prev和next不是同一個了,prev指向前面已經
        */
        final ActivityRecord next = topRunningActivityLocked();


        //這個變數是表示是否回呼Activity中的onUserLeaveHint和onUserInteraction函式
        final boolean userLeaving = mStackSupervisor.mUserLeaving;
        mStackSupervisor.mUserLeaving = false;

        final TaskRecord prevTask = prev != null ? prev.task : null;
        if (next == null) {//這個表示如果當前ActivityStack不存在待啟動的Activity,那么會啟動Launcher桌面
            final String reason = "noMoreActivities";
            final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack()
                    ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();

			// 當前AS不是全屏顯示,則需要將焦點切換到下一個待顯示的AS
            if (!mFullscreen && adjustFocusToNextFocusableStackLocked(returnTaskType, reason)) {
                return mStackSupervisor.resumeFocusedStackTopActivityLocked(
                        mStackSupervisor.getFocusedStack(), prev, null);
            }

            
            ActivityOptions.abort(options);

			// 默認情況下,Stack都是占據全屏的,所以,當前Stack如果沒有要顯示的Activity,則會要求顯示桌面
            return isOnHomeDisplay() &&
                    mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
        }

        next.delayedResume = false;


        //檢查要啟動的Activity 組件是否等于當前被激活的 Activity 組件,如果等于
        //并且處于 RESUMED 狀態,直接回傳,我們前面演示的啟動情況很顯然不滿足條件
        if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                    mStackSupervisor.allResumedActivitiesComplete()) {
             //當前正在顯示的Activity正好就是下一個待顯示的Activity,
            // 那么,就中斷對目標ActivityRecord的調度
            mWindowManager.executeAppTransition();
            mNoAnimActivities.clear();
            ActivityOptions.abort(options);
            return false;
        }

        final TaskRecord nextTask = next.task;
         /*這個是對上一個resumed的Activity的相關處理
		 * 由于我們是第一次啟動B Activity,所以不可能處于finish跳過此處
		 */
        if (prevTask != null && prevTask.stack == this &&
                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
			...
        }

        // 系統進入休眠狀態,當前Stack的堆疊頂Activity已經處于Paused狀態
        // 那么,中斷待顯示Activity的相關調度(有點拗口,學習原始碼就是這么枯燥的事情)
        if (mService.isSleepingOrShuttingDownLocked()
                && mLastPausedActivity == next
                && mStackSupervisor.allPausedActivitiesComplete()) {
            mWindowManager.executeAppTransition();
            mNoAnimActivities.clear();
            ActivityOptions.abort(options);
            return false;
        }

		...

		/*
			在ASS中存在很多的資料結構,用來統一管理ActivityRecord的狀態
	    	譬如mStoppingActivities記錄了當前所有處于Stopping狀態的ActivityRecord
	    	mGoingToSleepActivities記錄了當前所有要進入休眠狀態的ActivityRecord
	    	在某些場景下,待顯示的ActivityRecord可能處于這些陣列中,但需要從中剔除
		*/
        mStackSupervisor.mStoppingActivities.remove(next);
        mStackSupervisor.mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mStackSupervisor.mWaitingVisibleActivities.remove(next);

        

         // 如果當前ASS中還有ActivityRecord不是處于PAUSED, STOPPED或STOPPING這三個狀態之一,
    	// 那么,需要先等這些ActivityRecord進入停止狀態
        if (!mStackSupervisor.allPausedActivitiesComplete()) {
            return false;
        }

??分析至此,讓我們先緩緩,停下前進的腳步看看我們來時的路!前面的代碼片段,主要是做一些初始化和可能的"例外"處理作業,雖然我們待顯示的目標ActivityRecord已經位于堆疊頂,但要真正將其顯示到前臺來,即執行目標Activity的onCreate/onStart/onResume等狀態,這一路有很多障礙和初始化作業還處理,或者說還有很多前提條件需要滿足,譬如,系統要休眠時,當前啟動目標Activity程序要中斷;當前ASS中有Activity正處于Pausing狀態時,也需要等相關Activity執行完畢才行,我們可以將上述的相關作業認為是準備階段!前路漫漫是征途,我將上下而求索!

//[ActivityStack.java]
		/*
			setLaunchSource設定待啟動的Activity的資訊
			跟進setLaunchSource原始碼發現它最侄訓獲取一個WakeLock,保證在顯示Activity的程序中,系統不會進行休眠狀態
		*/
        mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
        

         /*
           目標Activity的啟動引數中是否包含FLAG_RESUME_WHILE_PAUSING
   		   如果存在FLAG_RESUME_WHILE_PAUSING的flag,表示可以在當前顯示的發起端Activity執行Pausing時,
   		   能同時進行Resume操作
    	   即變數dontWaitForPause的取意就是不需要等到Activity執行Pause完畢
    	*/
        final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;


		/* 
		  這個是pause掉不是FocusedStack的其它ActivityStack的堆疊頂activity
 		  對于不是當前focusStack的并且存在有mResumedActivity不為null的都要paused
 		  此時下沒有需要要進行pause
 		*/
		boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);

		//此時要帶入真是場景了,此時的mResumedActivity表示目標Stack堆疊中處于Resume狀態的Activity,通過前面的分析可以此時沒有處于Resume狀態的Activty了,所以不會走入此分支
		if (mResumedActivity != null) {
			....
        }
        if (pausing) {//也不會進入此分支可以往后續分析了
            if (next.app != null && next.app.thread != null) {
                mService.updateLruProcessLocked(next.app, true, null);
            }
            return true;
        }
         //檢查要啟動的Activity 組件是否等于當前被激活的 Activity 組件,如果等于
        //并且處于 RESUMED 狀態,直接回傳,我們前面演示的啟動情況很顯然不滿足條件 
        else if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                mStackSupervisor.allResumedActivitiesComplete()) {
            mWindowManager.executeAppTransition();
            mNoAnimActivities.clear();
            ActivityOptions.abort(options);
            return true;
        }

??在前面章節1.3的最后我們知道當前臺Activity被執行pause以后,會回呼activityPaused通知AMS,然后AMS會執行completePauseLocked,該函式也會呼叫resumeTopActivityInnerLocked,這一次,由于所有resumedActivity都已經paused了,所以回傳的結果pausing為false,所以可以繼續進行目標activity的resume作業,讓我們對相關的流程繼續分析!

//[ActivityStack.java]
		//對已經Pause的Activity繼續處理,主要是通知WMS做進一步的處理
        if (prev != null && prev != next) {
            if (!mStackSupervisor.mWaitingVisibleActivities.contains(prev)
                    && next != null && !next.nowVisible) {
                mStackSupervisor.mWaitingVisibleActivities.add(prev);
            } else {
                if (prev.finishing) {
                    mWindowManager.setAppVisibility(prev.appToken, false);
                } else {
                }
            }
        }


        try {
        	 // 通過PackageManager修改待啟動Package的狀態
            AppGlobals.getPackageManager().setPackageStoppedState(
                    next.packageName, false, next.userId); /* TODO: Verify if correct userid */
        } catch (RemoteException e1) {
        } catch (IllegalArgumentException e) {
        }

		...

        ActivityStack lastStack = mStackSupervisor.getLastStack();
		
        if (next.app != null && next.app.thread != null) {//如果目的端行程已經創建,即要啟動的目標Activity所屬行程已經存在
			...
            next.state = ActivityState.RESUMED;
            mResumedActivity = next;
            next.task.touchActiveTime();
            mRecentTasks.addLocked(next.task);
            mService.updateLruProcessLocked(next.app, true, null);
            updateLRUListLocked(next);
            mService.updateOomAdjLocked();
			...
            try {
			
				...

                next.sleeping = false;
                mService.showUnsupportedZoomDialogIfNeededLocked(next);
                mService.showAskCompatModeDialogLocked(next);
                next.app.pendingUiClean = true;
                next.app.forceProcessStateUpTo(mService.mTopProcessState);
                next.clearOptionsLocked();
				//執行目的端Activity的scheduleResumeActivity操作
                next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                        mService.isNextTransitionForward(), resumeAnimOptions);
				...
            } catch (Exception e) {
				...
            }

            try {
                completeResumeLocked(next);
            } catch (Exception e) {
				//處理例外
                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                        "resume-exception", true);
                return true;
            }
        } else {//當目標Activity所屬行程沒有啟動的時候,則會創建行程

            if (!next.hasBeenLaunched) {
                next.hasBeenLaunched = true;
            } else {
                if (SHOW_APP_STARTING_PREVIEW) {
                    next.showStartingWindow(null, true);
                }
            }
			
			//創建目標Activity行程
            mStackSupervisor.startSpecificActivityLocked(next, true, true);

			
        }


        return true;
    }

??到這里終于快要告一段落了,此處的邏輯主要分為兩個分支:

  • 如果要啟動的目標Activity所屬行程已經創建,則直接通過ATP呼叫目標行程的ActivityThread執行相關的Activity的onCreate等相關生命周期
  • 如果目標Activity所屬行程沒有創建,則通過startSpecificActivityLocked方法創建目標行程,經過層層呼叫最后會呼叫到AMS.attachApplicationLocked, 然后再執行resumeTopActivityInnerLocked繼續resume操作這個邏輯我想也是小伙們最關心的, 我們后續從這個地方開擼

1.5 Pause前臺顯示Activity,Resume目標Activity小結

??Pause前臺顯示Activity,Resume目標Activity到這里就基本完結了,是時候停下前進的腳步回過頭來看看了!在上述程序中我們會進行兩次resumeTopActivityInnerLocked方法:

  • 第一次是將所有resumedActivity進行pause,此時流程不會繼續往下進行而是待前臺顯示的Activity真正執行pause后,然后回呼AMS繼續第二執行resumeTopActivityInnerLocked相關操作
  • 由于此時所有處于Resume狀態的Activity已經都被Pause了,所以繼續往下執行,此時會判斷目標Activity所屬行程是否創建,如果創建則直接執行目標Activity的生命周期,如果沒有創建則會創建目標Activity所屬行程,進而再執行下一步操作

對于上述的整個流程,可以使用下述的圖示來表達:

在這里插入圖片描述

其中紅色箭頭表示Binder跨行程呼叫
黃色框表示的是發起端行程
紫色框表示的是AMS所屬system_server行程
紅色框表示的是目標Activity所屬行程
藍色框表示的前臺處于Resume的Activity所屬行程


總結

??Activity啟動流程(四)- Pause前臺顯示Activity,Resume目標Activity這里就要告一段落了,從前面的分析可以看出來,此時我們已將將需要Pause前臺Activity已經Pause,接下來就是專心的來Resume目標Activity了,如果此時是冷啟動的目標Activity那么就會先期進行目標Activity目標所屬行程的創建,然后接著繼續Resume目標Activity,如果是熱啟動就簡單一些了直接執行目標Activity相關的onCretate等相關的操作,好了今天就到這里了,是時候說再見了!希望小伙們能點贊和關注,謝謝!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/174901.html

標籤:其他

上一篇:Android Studio類微信界面

下一篇:IDEA配置JVM引數

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more