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

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

2020-10-16 18:13:22 其他

???? 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/qita/174539.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)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more