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

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

2020-11-05 17:05:31 區塊鏈

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/qukuanlian/203488.html

標籤:區塊鏈

上一篇:Activity LaunchMode決議

下一篇:Android Studio 設定應用圖示的不同解析度

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

熱門瀏覽
  • JAVA使用 web3j 進行token轉賬

    最近新學習了下區塊鏈這方面的知識,所學不多,給大家分享下。 # 1. 關于web3j web3j是一個高度模塊化,反應性,型別安全的Java和Android庫,用于與智能合約配合并與以太坊網路上的客戶端(節點)集成。 # 2. 準備作業 jdk版本1.8 引入maven <dependency> < ......

    uj5u.com 2020-09-10 03:03:06 more
  • 以太坊智能合約開發框架Truffle

    前言 部署智能合約有多種方式,命令列的瀏覽器的渠道都有,但往往跟我們程式員的風格不太相符,因為我們習慣了在IDE里寫了代碼然后打包運行看效果。 雖然現在IDE中已經存在了Solidity插件,可以撰寫智能合約,但是部署智能合約卻要另走他路,沒辦法進行一個快捷的部署與測驗。 如果團隊管理的區塊節點多、 ......

    uj5u.com 2020-09-10 03:03:12 more
  • 谷歌二次驗證碼成為區塊鏈專用安全碼,你怎么看?

    前言 谷歌身份驗證器,前些年大家都比較陌生,但隨著國內互聯網安全的加強,它越來越多地出現在大家的視野中。 比較廣泛接觸的人群是國際3A游戲愛好者,游戲盜號現象嚴重+國外賬號安全應用廣泛,這類游戲一般都會要求用戶系結名為“兩步驗證”、“雙重驗證”等,平臺一般都推薦用谷歌身份驗證器。 后來區塊鏈業務風靡 ......

    uj5u.com 2020-09-10 03:03:17 more
  • 密碼學DAY1

    目錄 ##1.1 密碼學基本概念 密碼在我們的生活中有著重要的作用,那么密碼究竟來自何方,為何會產生呢? 密碼學是網路安全、資訊安全、區塊鏈等產品的基礎,常見的非對稱加密、對稱加密、散列函式等,都屬于密碼學范疇。 密碼學有數千年的歷史,從最開始的替換法到如今的非對稱加密演算法,經歷了古典密碼學,近代密 ......

    uj5u.com 2020-09-10 03:03:50 more
  • 密碼學DAY1_02

    目錄 ##1.1 ASCII編碼 ASCII(American Standard Code for Information Interchange,美國資訊交換標準代碼)是基于拉丁字母的一套電腦編碼系統,主要用于顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統,并等同于國際標準ISO/IE ......

    uj5u.com 2020-09-10 03:04:50 more
  • 密碼學DAY2

    ##1.1 加密模式 加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html ECB ECB : Electronic codebook, 電子密碼本. 需要加密的訊息按照塊密碼的塊大小被分為數個塊,并對每個塊進 ......

    uj5u.com 2020-09-10 03:05:42 more
  • NTP時鐘服務器的特點(京準電子)

    NTP時鐘服務器的特點(京準電子) NTP時鐘服務器的特點(京準電子) 京準電子官V——ahjzsz 首先對時間同步進行了背景介紹,然后討論了不同的時間同步網路技術,最后指出了建立全球或區域時間同步網存在的問題。 一、概 述 在通信領域,“同步”概念是指頻率的同步,即網路各個節點的時鐘頻率和相位同步 ......

    uj5u.com 2020-09-10 03:05:47 more
  • 標準化考場時鐘同步系統推進智能化校園建設

    標準化考場時鐘同步系統推進智能化校園建設 標準化考場時鐘同步系統推進智能化校園建設 安徽京準電子科技官微——ahjzsz 一、背景概述隨著教育事業的快速發展,學校建設如雨后春筍,隨之而來的學校教育、管理、安全方面的問題成了學校管理人員面臨的最大的挑戰,這些問題同時也是學生家長所擔心的。為了讓學生有更 ......

    uj5u.com 2020-09-10 03:05:51 more
  • 位元幣入門

    引言 位元幣基本結構 位元幣基礎知識 1)哈希演算法 2)非對稱加密技術 3)數字簽名 4)MerkleTree 5)哪有位元幣,有的是UTXO 6)位元幣挖礦與共識 7)區塊驗證(共識) 總結 引言 上一篇我們已經知道了什么是區塊鏈,此篇說一下區塊鏈的第一個應用——位元幣。其實先有位元幣,后有的區塊 ......

    uj5u.com 2020-09-10 03:06:15 more
  • 北斗對時服務器(北斗對時設備)電力系統應用

    北斗對時服務器(北斗對時設備)電力系統應用 北斗對時服務器(北斗對時設備)電力系統應用 京準電子科技官微(ahjzsz) 中國北斗衛星導航系統(英文名稱:BeiDou Navigation Satellite System,簡稱BDS),因為是目前世界范圍內唯一可以大面積提供免費定位服務的系統,所以 ......

    uj5u.com 2020-09-10 03:06:20 more
最新发布
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

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

    uj5u.com 2023-04-20 08:46:47 more
  • Hyperledger Fabric 使用 CouchDB 和復雜智能合約開發

    在上個實驗中,我們已經實作了簡單智能合約實作及客戶端開發,但該實驗中智能合約只有基礎的增刪改查功能,且其中的資料管理功能與傳統 MySQL 比相差甚遠。本文將在前面實驗的基礎上,將 Hyperledger Fabric 的默認資料庫支持 LevelDB 改為 CouchDB 模式,以實作更復雜的資料... ......

    uj5u.com 2023-04-16 07:28:31 more
  • .NET Core 波場鏈離線簽名、廣播交易(發送 TRX和USDT)筆記

    Get Started NuGet You can run the following command to install the Tron.Wallet.Net in your project. PM> Install-Package Tron.Wallet.Net 配置 public reco ......

    uj5u.com 2023-04-14 08:08:00 more
  • DKP 黑客分析——不正確的代幣對比率計算

    概述: 2023 年 2 月 8 日,針對 DKP 協議的閃電貸攻擊導致該協議的用戶損失了 8 萬美元,因為 execute() 函式取決于 USDT-DKP 對中兩種代幣的余額比率。 智能合約黑客概述: 攻擊者的交易:0x0c850f,0x2d31 攻擊者地址:0xF38 利用合同:0xf34ad ......

    uj5u.com 2023-04-07 07:46:09 more
  • Defi開發簡介

    Defi開發簡介 介紹 Defi是去中心化金融的縮寫, 是一項旨在利用區塊鏈技術和智能合約創建更加開放,可訪問和透明的金融體系的運動. 這與傳統金融形成鮮明對比,傳統金融通常由少數大型銀行和金融機構控制 在Defi的世界里,用戶可以直接從他們的電腦或移動設備上訪問廣泛的金融服務,而不需要像銀行或者信 ......

    uj5u.com 2023-04-05 08:01:34 more
  • solidity簡單的ERC20代幣實作

    // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "hardhat/console.sol"; //ERC20 同質化代幣,每個代幣的本質或性質都是相同 //ETH 是原生代幣,它不是ERC20代幣, ......

    uj5u.com 2023-03-21 07:56:29 more
  • solidity 參考型別修飾符memory、calldata與storage 常量修飾符C

    在solidity語言中 參考型別修飾符(參考型別為存盤空間不固定的數值型別) memory、calldata與storage,它們只能修飾參考型別變數,比如字串、陣列、位元組等... memory 適用于方法傳參、返參或在方法體內使用,使用完就會清除掉,釋放記憶體 calldata 僅適用于方法傳參 ......

    uj5u.com 2023-03-08 07:57:54 more
  • solidity注解標簽

    在solidity語言中 注釋符為// 注解符為/* 內容*/ 或者 是 ///內容 注解中含有這幾個標簽給予我們使用 @title 一個應該描述合約/介面的標題 contract, library, interface @author 作者的名字 contract, library, interf ......

    uj5u.com 2023-03-08 07:57:49 more
  • 評價指標:相似度、GAS消耗

    【代碼注釋自動生成方法綜述】 這些評測指標主要來自機器翻譯和文本總結等研究領域,可以評估候選文本(即基于代碼注釋自動方法而生成)和參考文本(即基于手工方式而生成)的相似度. BLEU指標^[^?88^^?^]^:其全稱是bilingual evaluation understudy.該指標是最早用于 ......

    uj5u.com 2023-02-23 07:27:39 more
  • 基于NOSTR協議的“公有制”版本的Twitter,去中心化社交軟體Damus

    最近,一個幽靈,Web3的幽靈,在網路游蕩,它叫Damus,這玩意詮釋了什么叫做病毒式營銷,滑稽的是,一個Web3產品卻在Web2的產品鏈上瘋狂傳銷,各方大佬紛紛為其背書,到底發生了什么?Damus的葫蘆里,賣的是什么藥? 注冊和簡單實用 很少有什么產品在用戶注冊環節會有什么噱頭,但Damus確實出 ......

    uj5u.com 2023-02-05 06:48:39 more