主頁 > 後端開發 > Android四大組件之Activity啟動流程原始碼實作詳解(四)

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

2020-10-18 05:10:31 後端開發

???? 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操作這個邏輯我想也是小伙們最關心的, 我們后續從這個地方開擼

注意,注意,注意:
Activity的啟動程序中,存在一種特殊情況就是假如目標Activity在AndroidManifest.xml中配置了android:process相關的屬性,那怕目標Activity所屬行程已經創建了依然會走startSpecificActivityLocked流程創建Activity專有的Process,這個一定要注意!

        <activity android:name="com.example.test.BActivity"
            android:process=":process"
             >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

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/houduan/177402.html

標籤:python

上一篇:Android Studio類微信界面

下一篇:三年啦,跳槽成功的Android開發面經總結!

標籤雲
其他(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)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more