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

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

2020-11-05 00:48:57 其他

Activity啟動流程原始碼實作(七)初始化目標Activity并執行相關生命周期流程


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

Android應用行程創建流程大揭秘
Android四大組件之bindService原始碼實作詳解
Android四大組件之Activity啟動流程原始碼實作詳解概要
Android四大組件之Activity啟動流程原始碼實作詳解(一)
Android四大組件之Activity啟動流程原始碼實作詳解(二)
Activity啟動流程(三)- Activity Task調度演算法復盤分析
Activity啟動流程(四)- Pause前臺顯示Activity,Resume目標Activity
Activity啟動流程(五)- 請求并創建目標Activity行程
Activity啟動流程(六)- 注冊目標Activity行程到system_server行程以及創建目標Activity行程Application
Activity啟動流程(七)- 初始化目標Activity并執行相關生命周期流程


前言

??不知不覺Activity啟動系列博客已經更新到系列七來了,這其中的不容易和艱辛只有自己知道了,前面的幾章主要是重點分析了Activity啟動程序中的涉及的前兩個階段請求階段行程創建階段,上述兩個階段成果為我們的Activity的啟動和顯示構建好了穩定的外部環境和載體,但是革命尚未成功同志仍需努力啊!在這一篇章中我們將重點介紹Android是怎么開啟目標Activity生命周期如下小階段的:

  • 真正啟動目的端Activity
  • 通過反射加載目標Activity
  • 執行目標Activity生命周期
  • 初始化目標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

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

在這里插入圖片描述

我們對上述的時序圖簡單的來注釋一下:
綠色的組件表示的是運行在目標Activity所屬行程中
藍色的組件表示是運行在system_server行程中
Activity所屬行程和system_server行程之間通過紅色箭頭表示的Binder完成了跨行程通信,配合完成了Activity啟動的下半場接力賽
紫色箭頭表示的是Handler內部資訊傳遞



一. system_server行程真正的啟動目標Activity

??看到這個標題也許有小伙們心里想了,難不成我們搞了這么久前面的博客都是假啟動目標Activity了,這個當然不是了!怎么說呢,打個不恰當的比喻啊,這個就好像你找女朋友,找老婆必須有個前期儲備建立感情基礎不,而我們前面所做的就是為了此時真正的啟動目標Activity打下的夯實基礎而已!

好了,我們繼續回到前面博客Activity啟動流程(六)- 注冊目標Activity行程到system_server行程以及創建目標Activity行程Application的章節1.3,在我們的目標Actiivty應用行程起來之后,會繼續檢查有沒有四大組件在等待運行,如果有會繼續其運行(這里我么只關注Activity的啟動),


1.1 ASS.attachApplicationLocked(…)

??尼瑪!這里又是一個attachApplicationLocked方法,是不是搞錯了,真沒有錯!最外層的attachApplicationLocked是針對AMS的,而這里的attachApplicationLocked方法是針對ASS的,

該方法取名為attachApplication(),顧名思義就是給ASS系結一個應用,那么,ASS中有什么需要被系結到一個應用行程的呢?如果小伙們是從Actiivity啟動的請求階段一路走過來那就知道當然是AcivityRecord了,我們在啟動目標Activity時創建或者復用了一個新的ActivityRecord,并將其挪到了Stack的堆疊頂位置,當時,ActivityRecord還沒有關聯到任何行程相關的資訊,還不能被遷移到顯示狀態,當應用行程被創建之后,Activity才有了運行的機會,這時候才會真正調度我們前面的ActivityRecord,

//[ActivityStackSupervisor.java]
    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = 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)) {
                    continue;
                }
                /*
                	獲取堆疊定的ActivityRecord
                	此時經過Activity請求階段一系列的作業,已經把Activity所屬的Task和ActivityStack移動到頂了
                */
                ActivityRecord hr = stack.topRunningActivityLocked();
                if (hr != null) {
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {
                        	//詳見章節1.2
                            if (realStartActivityLocked(hr, app, true, true)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
							...
                        }
                    }
                }
            }
        }
        return didSomething;
    }

該方法的實作邏輯與我們前面博客中介紹的ASS的其他方法基本類似:

  • 遍歷尋找所有ActivityStack和TaskRecord,然后找到目標Activity的ActivityRecord
  • 對堆疊頂的目標ActivityRecord進行操作,將上述作為引數繼續呼叫realStartActivityLocked真正啟動目標Activity

這里的realStartActivityLocked是不是有點似曾相識的感覺!是的,在前面博客中Activity啟動流程(五)- 請求并創建目標Activity行程的2.1章節中的startSpecificActivityLocked我們有見過它的身影,并且當時的判斷條件是是如果目標Activity所屬行程已經創建了的話就會直接進入此分支(如果對于此處有所遺忘的小伙們,可以回頭看看),看來它很重要啊,我們來強烈圍觀一下!


1.2 ASS.realStartActivityLocked(…)

??此方法很霸氣啊,真的啟動Activity!感覺有點像我們小時候過年走親戚,快要出門的時候大聲對主人家說,我真的要告辭了啊(意外之意就是主人家,得準備好紅包給我們小孩了)!而這里的弦外之音,我們可以理解我就是要開啟Activity的相關生命周期了,即我們應用開發者所熟知的onCreate()/onStart()等方法的調度,這應該就是這里真正啟動Activity的真正含義嗎!

//[ActivityStackSupervisor.java]
    final boolean realStartActivityLocked(ActivityRecord r, //目標Activity資料資訊
    									  ProcessRecord app,//目標Activity所屬行程資訊
            							  boolean andResume, //是否執行resume,傳入的引數值為true
            							  boolean checkConfig)//是否檢查Config配置,傳入的引數值為true
            							  throws RemoteException {

		...
		r.app = app;
		...
        int idx = app.activities.indexOf(r);
         //判斷當前要啟動的Activty是否在ProcessRecord中有相關的記錄,如若沒有則把它加到ProcessRecord中
        if (idx < 0) {
            app.activities.add(r);
        }
        mService.updateLruProcessLocked(app, true, null);//更新行程Lru
        mService.updateOomAdjLocked();//更新行程adj優先級        
        ...
		
		/*
			此處的套路,小伙們應該見怪不怪了嗎,前面的博客中有多處用到
			通過IApplicationThread匿名Binder呼叫到目標Activity行程的ActivityThread中
		*/
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
              System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
              new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
              task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
              newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);//詳見章節2.1
		...
        if (andResume) {
            /*
            	此時目標Activity已經呼叫完onResume()方法了,
            	所以這時候需要將目標Activity所屬的ActivityStack堆疊中的mResumedActivity
            	設定目標Activity,并且狀態置為RESUMED
            */
            stack.minimalResumeActivityLocked(r);
            /*************************************************************/
            	//這里為了方便演示,將上述原始碼就地展開
            	//[ActivitStack.java]
			    void minimalResumeActivityLocked(ActivityRecord r) {
			        r.state = ActivityState.RESUMED;//設定為RESUMED狀態
			        mResumedActivity = r;	//并將ActivityRecord賦給對應的stack的mResumedActivity 
			        ...
			    }            
            /*************************************************************/
        } else {
			...
        }
		...
		return true;
    }

realStartActivityLocked()方法的邏輯不少,我們這里抽絲剝繭只取一瓢,重點關注如下三點:

  • 將目標ActivityRecord和對應的ProcessRecord關聯起來,即通過ActivityRecord可以找到對應的ProcessRecord,然后將目標ActivityRecord存入到ProcessRecord資料結構中的activities串列中!真是你中有我,我中有你的典范啊!
  • 借助ATP跨行程呼叫AT中的scheduleLaunchActivity()方法,進而調度啟動目標Activity(這個是重點)
  • 目標Activity已經呼叫完onResume()等生命周期流程之后,這時將目標Activity所屬的ActivityStack堆疊中的mResumedActivity設定目標Activity,并且狀態置為RESUMED,為后續的Activity相關調度構建好環境


二. 創建并加載目標Activity

??如上一切執行妥當以后,system_server行程會通過IApplicationThread跨行程回呼到目標Activity應用行程,從而開始驅動目標Actiivity生命周期的運轉和執行了,至于IApplicationThread是怎么實作跨行程呼叫的這里我也不贅述了(這個當然是Binder的功能,分析Android原始碼Binder是一道必須跨過去的坎啊,并且這個呼叫的流程在該系列前面的博客中也有詳細的分析過,這里就不贅述了,看的多了,不理解應該也大概能懂了)!總之通過app.thread.scheduleLaunchActivity實際上呼叫的就是目標Activity行程中ActivityThread的內部類ApplicationThread的scheduleLaunchActivity方法中去了,還是放上一下關于IApplicationThread類圖關系圖嗎(這個冷飯都抄了很多次了)!

在這里插入圖片描述

其呼叫程序可以使用下面的偽代碼來表示:

ATP.scheduleLaunchActivity()---> 
BinderProxy.transact() --->
BpBinder.transact()--->

binder驅動傳輸--->

JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.scheduleLaunchActivity(...) --->
ActivityThread.ApplicationThread.scheduleLaunchActivity(...) --->

2.1 ActivityThread.ApplicationThread.scheduleLaunchActivity(…)

//[ActivityThread.java]
    private class ApplicationThread extends ApplicationThreadNative {
    	...
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);
			//將AMS傳遞過來啟動目標Activity相關的引數填充ActivityClientRecord 資料結構
            ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }    	
    	...
    }

此時的scheduleLaunchActivity()方法在ApplicationThread的Binder執行緒中執行,在該方法中將AMS傳遞過來的引數填充ActivityClientRecord資料結構,然后通過ActivityThread.H攜帶上述引數呼叫到ActivityThread的主執行緒中執行處理,而處理上述邏輯的即是handleLaunchActivity()方法!

在ActivityThread中有如下的一個套路,通常AMS中通過Binder遠程呼叫的scheduleXXX方法,經過Handler的傳遞以后通常會在主執行緒中的handleXXX進行下一步的處理!

我們接著繼續分析,看看handleLaunchActivity()的處理邏輯!


2.2 ActivityThread.handleLaunchActivity(…)加載并開啟Activity生命周期

前面的一切付出都是值得的,進入此流程就要真正的加載并開啟目標Activity的生命周期了,付出了這么多終于見到了回報和勝利的輸光了!

//[ActivityThread.java]
    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        unscheduleGcIdler();//GC相關處理
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        //最侄訓呼目標Activity的onConfigurationChanged()
        handleConfigurationChanged(null, null);

		//獲取WMS服務代理端
        WindowManagerGlobal.initialize();
		//加載目標Activity,并最侄訓呼目標Activity的onCreate
        Activity a = performLaunchActivity(r, customIntent);//詳見章節2.3

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            //最侄訓呼目標Activity的onStart,onResume.
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);//詳見章節2.4

            if (!r.activity.mFinished && r.startsNotResumed) {//這個正常的啟動目標Activity不會進入此分支
				...
            }
        } else {
        		//如果啟動目標Activity發生例外則直接finish掉目標Activity
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        }
    }

這里handleLaunchActivity()方法處理的邏輯是什么呢,大概可以分為如下幾部分:

  • 首先做一些基本的初始化引數設定作業
  • 接著呼叫performLaunchActivity()方法加載目標Activity,并最侄訓呼目標Activity的onCreate()等生命周期方法
  • 如果上述步驟執行成功,繼續呼叫handleResumeActivity()方法來處理Activity進入顯示狀態時需要完成的操作
  • 如果步驟二的操作失敗,則通知AMS將目標Activity直接finish掉

2.3 ActivityThread.performLaunchActivity(…)加載Activity

這個方法是真正的負責執行目標Activity的加載和啟動,是system_server行程負責調度目標Activity啟動的落腳點,從此處開始真正進入我們所熟知的Activity生命周期相關流程!

//[ActivityThread.java]
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
		//開始構建目標Activity相關資訊
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        Activity activity = null;
        try {
        	/*
        		通過反射加載目標Activity
        		這里獲取的ClassLoader是前面博客已經初始化了的
        	*/
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            /*****************************************************/
            	//這里為了演示方便,直接把原始碼展開
            	//[Instrumentation.java]
			    public Activity newActivity(ClassLoader cl, String className,
			            Intent intent)
			            throws InstantiationException, IllegalAccessException,
			            ClassNotFoundException {
			        return (Activity)cl.loadClass(className).newInstance();
			    }            	
            /*****************************************************/      
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
			...
        }
        try {
        	/*
        		創建目標Actiivty應用行程Application,目標Application在前面博客中已經有被創建了,而且
        		應用行程的Application是唯一的,所以會直接回傳前面創建的Applcation
        	*/
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
            	//創建目標Activity對應的Context背景關系
                Context appContext = createBaseContextForActivity(r, activity);
                /*****************************************************/ 
                		//[ActivityThread.java]
                		//這里為了演示方便,將其擴展開
					    private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
					        int displayId = Display.DEFAULT_DISPLAY;
					        try {
					            displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
					        } catch (RemoteException e) {
					            throw e.rethrowFromSystemServer();
					        }
					
					        ContextImpl appContext = ContextImpl.createActivityContext(
					                this, r.packageInfo, r.token, displayId, r.overrideConfig);
					        appContext.setOuterContext(activity);
					        Context baseContext = appContext;
					
					        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
					        String pkgName = SystemProperties.get("debug.second-display.pkg");
					        if (pkgName != null && !pkgName.isEmpty()
					                && r.packageInfo.mPackageName.contains(pkgName)) {
					            for (int id : dm.getDisplayIds()) {
					                if (id != Display.DEFAULT_DISPLAY) {
					                    Display display =
					                            dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
					                    baseContext = appContext.createDisplayContext(display);
					                    break;
					                }
					            }
					        }
					        return baseContext;
					    }                
                /*****************************************************/ 
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }

                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }   
                //將上述創建的相關資訊,attach到Activity中為后續Activity顯示運行做準備     
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {//如果在AndroidManifest中有配置Activity主題,則設定主題
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                //開始執行目標Activity的onCreate()方法回呼
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }   
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();//執行目標Activity的onStart()方法
                    r.stopped = false;
                }
				...
            }
            r.paused = true;
			//將目標Activity相關資訊,保存到mActivities中
            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {        
            throw e;

        } catch (Exception e) {
			...
        }

        return activity;
    }                             

我們的performLaunchActivity()方法有干了那些事情,有那些功勞呢,我們來掰持掰持:

  • 首先會初始化構建目標Activity相關的一些資訊,此處我們目標Activity的創建是通過反射來構建的,這就需要包名、類名等資訊,而其中的絕大部分資訊已經從系統行程傳遞到應用行程了,還有些部分資訊也都可以通過PackageManager再向系統行程索取;

  • 上述相關引數準備完畢OK以后,便可以通過ClassLoader加載到Activity對應的類,通過反射加載

  • 接著依次創建目標Activity對應的Application物件,當前條件下并不會創建,因為前面已經有初始化過了Application了,而且Activity應用行程只會對應一個Application

  • 接著呼叫createBaseContextForActivity()方法繼續構建目標Activity對應的Context背景關系,這里的Activity的Context和前面我們Application的Context最大的不同在于Application的Context只包含了packageInfo,而Activity的context的還包含了displayInfo,所以當需要將Context用在顯示相關的場合時,請使用Activtiy作為Context,

    不知道我上面的表達清楚了沒有!一句話總結就是Application中的Context是不能用來做和UI顯示相關的操作!

  • 接著呼叫目標Activity的attach()將前面創建相關的資料結構,傳遞到目標Activity中

  • 接著如果在AndroidManifest中有配置了目標Activity主題,則設定相關主題,否則使用默認的

  • 接著呼叫callActivityOnCreate()方法,進而開始執行目標Activity物件的onCreate方法,至此Actiivty進入其生命周期的開始

  • 接著呼叫performStart(),開始目標Activity生命周期的另一征程onStart()方法

    分析至此,小伙們是否有一種酣暢淋漓的冠絕,經過了千辛萬苦,總算到Activity.onCreate()這個方法啊!原來我們在應該用開發中的簡簡單單的幾行代碼,在Android系統中是經過了這樣一番曲折的程序才會被執行!

    Activity生命周期相關方法就像是Activity調度程序的冰山一角,對于做應用層的開發人員而言,只需要看到最外層的冰山,就能栩栩如生地把冰山的樣子轉述出來;然而,意想不到的是,冰山底下宏大的世界,撐起整座冰山的根基卻是如此的復雜,為了冰山一角我們的谷歌的哥哥姐姐可沒有少做作業啊!


2.4 ActivityThread.handleResumeActivity(…)執行目標Activity的onResume

前面相關的方法執行成功以后,我們的目標Activity物件也已經構建成功,并成功執行完onCreate(), onStart()兩個生命周期的調度之后,便進入到這個階段,執行目標Activity的onResume()生命周期的調度!

//[ActivityThread.java]
	final void handleResumeActivity(IBinder token,
	        boolean clearHide, boolean isForward, boolean reallyResume) {
	        
	    ActivityClientRecord r = mActivities.get(token);
	    unscheduleGcIdler();
	    mSomeActivitiesChanged = true;
	
	    // 實際執行應用行程一側的Activity.onResume()
	    r = performResumeActivity(token, clearHide);
	    if (r != null) {
	        ... // 省略與WindownManager相關的視窗操作
	        if (reallyResume) {
	            // 通知系統行程,Activity已經處于Resumed狀態
	            ActivityManagerNative.getDefault().activityResumed(token);
	        }
	    }
	}

這里真正呼叫目標Activity生命周期的onResume()方法的是performResumeActivity()方法,在這里就不展開分析了,至此我們的整個的Actiivty啟動流程到此就結束了!



三. Application/LoadedApk/ContextImpl/Activity關系小結

分析到這里,Activity啟動程序中目標Activity應用行程端涉及的相關幾個重要角色幾乎都已經隆重登場了,特別是Application,LoadedApk,ContextImpl,Activity,而且在前面博客和本篇的博客中都有重點介紹上述幾個概念,這里我們有必要梳理梳理這幾者之間的關系,其幾者之間關系是剪不斷理還亂,但是大致上關系如下:

在這里插入圖片描述

  • Application:在一個App應用行程中是有且唯一的,整個應用行程只有一個實體,它和Activity對應的關系是一對多,通常我們在應用程式開發程序中,當我們重寫了Application類后,應用程式加載運行的是我們定義的Application類,否則就加載運行默認的Application類,Application擁有自己對應的ContextImpl,它擁有的ContextImpl和Activity對應的ContextImpl的區別就是Application物件所擁有的ContextImpl背景關系沒有對應的Token和相關顯示的displayId,所以Application對應的Applcation不能用于UI相關的顯示(Toast除外),否則會拋出如下例外!

在這里插入圖片描述

  • ContextImpl:這個我們在前面有重點介紹分析過,每個啟動的Activity都擁有屬于自身的ContextImpl物件,并且Application也擁有自己對應的!ContextImpl記錄了應用程式的包名資訊、應用程式的資源資訊、應用程式的主執行緒、ContentResolver及Activity對應的IApplicationToken.Proxy,但是Application物件所擁有的ContextImpl背景關系就沒有對應的Token了!

  • LoadedApk:從我們前面的分析可知LoadedApk類記錄了Activity運行所在的ActivityThread、Activity所在的應用程式資訊、Activity所在應用行程的包名、Activity所在應用行程的資源路徑、Activity應用行程的庫路徑、Activity所在應用行程的資料存盤路徑、類加載器和應用程式所使用的資源等資訊,并且在一個App應用行程中通常是有且唯一的!

  • Activity:它就不需要我過多介紹了,通常學習Android開發的小伙們最先認識的就是它了,它是我們Android世界的顏值擔當,負責界面的呈現!它和ContextImpl是一對一的關系的,每個Activity都擁有自己的ContextImpl!

如上就是幾者之間的牽扯關系了,這個大伙在分析原始碼的時候可以重點關注關注,并且它們之間牽涉了很多的相互參考,理解好了上述的關系對我們理解原始碼大有裨益!



總結

??到此,目標Activity便正式啟動,開始進入Activity生命周期,執行完onCreate/onStart/onResume方法,然后開始UI渲染,經過上述一番操作之后目標Activity就可以顯示到我們的主界面了!

而到此,我們的Android四大組件之Activity啟動流程也就就此完結了!在最后我們從行程的互動角度出發,對我們整個Activity啟動流程簡單總結一番!在總結之前我們先看它涉及的整體流程圖:

在這里插入圖片描述

目標Actiivyt啟動流程如下:

  • 發起端行程(可能是第三方App行程也可能是桌面Launcher)呼叫Binder IPC跨行程通信機制向system_server行程發起startActivity目標Activity請求
  • system_server行程在收到發起端行程的請求之后,會做一系列處理為目標Actiivity找到/創建合適的stack和TaskRecord堆疊,創建目標Actiivty對應的ActivityRecord資訊,并且pause前臺顯示的相關Actiivity
  • 我們這里的前提是目標Actiivyt應用行程還沒有創建,所以此時system_server行程在處理完上述一切之后,向zygote行程發出請求fork目標Actiivyt行程
  • 目標Actiivyt行程被創建之后,通過Binder IPC向sytem_server行程發起attachApplication請求,繼續向AMS注冊相關資料結構為后續目標Activity行程轉變成應用開發所熟知的App行程做相關準備
  • 接著system_server行程通過Binder IPC向目標Activity行程發起bindApplication請求
  • 目標Actiivyt行程的binder執行緒(ApplicationThread)在收到請求后,通過handler向主執行緒發送BIND_APPLICATION訊息
  • 主執行緒在收到Message后,通過發射機制創建目標Application,并回呼Application.onCreate()等方法
  • 上述執行OK以后,system_server行程會繼續檢查目標Activity行程是否還有Actiivyt待啟動(當然有了),接著再通過binder IPC向目標Actiivyt行程發送scheduleLaunchActivity請求;
  • 目標Activity行程的binder執行緒(ApplicationThread)在收到請求后,通過handler向主執行緒發送LAUNCH_ACTIVITY訊息
  • 主執行緒在收到Message后,通過發射機制創建目標Activity,并回呼Activity.onCreate()等方法


寫在最后

??Activity啟動流程(七)- 初始化目標Activity并執行相關生命周期流程這里就要告一段落了,整個的Activity的啟動也基本宣告完結了!雖然說是基本完結了,但是Activiyt啟動程序中涉及的很多知識點并不是到此就已經over了,這個就需要各位小伙們在平時的積累和總結了,在接下來的博客中,我將會重點分析目標Activity的是怎么加載布局以及怎么將布局繪制到我們的終端界面上面的,如果有感興趣的親請期待,好了,青山不改綠水長流先到這里了,如果本博客對你有所幫助,麻煩關注或者點個贊,如果覺得很爛也可以踩一腳!謝謝各位了!!

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

標籤:其他

上一篇:實驗二 Activity組件通信

下一篇:VMware安裝蘋果虛擬機-親測有效

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