日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

启动Activity的流程(Launcher中点击图标启动)

發(fā)布時(shí)間:2024/1/8 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 启动Activity的流程(Launcher中点击图标启动) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

啟動(dòng)Activity一般有多種方式,常見的有三種:

  • 在Launcher桌面點(diǎn)擊app圖標(biāo)

  • 調(diào)用startActivity啟動(dòng)一個(gè)Activity

  • 命令am start啟動(dòng)

  • 這三種方式在服務(wù)端的處理方式基本相同,客戶端的請(qǐng)求方式也差別不大,理解其中之一就可以類推到其他方式。本文結(jié)合案例分析在Launcher桌面點(diǎn)擊app圖標(biāo)啟動(dòng)應(yīng)用的方式,再簡(jiǎn)要給出其他兩種方式的區(qū)別。

    案例

    應(yīng)用名稱為TestLaunchApp,包含A和B兩個(gè)Activity,A為入口類,點(diǎn)擊按鈕跳轉(zhuǎn)到B

    A.java
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 packagecom.example.startapptest; import?android.app.Activity; import?android.content.Intent; import?android.os.Bundle; import?android.util.Log; import?android.view.View; publicclassAextendsActivity{ privatefinalstaticStringTAG="StartAppTest"; @Override protectedvoidonCreate(Bundle?savedInstanceState){ super.onCreate(savedInstanceState); Log.i(TAG,"A"+"--------------onCreate()"); setContentView(R.layout.a_layout); } @Override protectedvoidonResume(){ // TODO Auto-generated method stub Log.i(TAG,"A"+"--------------onResume()"); super.onResume(); } @Override protectedvoidonPause(){ // TODO Auto-generated method stub Log.i(TAG,"A"+"--------------onPause()"); super.onPause(); } publicvoidfuncA(View?view){ startActivity(newIntent("com.feeyan.www.b_activity")); } @Override publicvoidonBackPressed(){ // TODO Auto-generated method stub finish(); super.onBackPressed(); } }


    B.java
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 packagecom.example.startapptest; import?android.app.Activity; import?android.content.Intent; import?android.os.Bundle; import?android.util.Log; import?android.view.View; publicclassBextendsActivity{ privatefinalstaticStringTAG="StartAppTest"; @Override protectedvoidonCreate(Bundle?savedInstanceState){ super.onCreate(savedInstanceState); Log.i(TAG,"B"+"--------------onCreate()"); setContentView(R.layout.b_layout); } @Override protectedvoidonResume(){ Log.i(TAG,"B"+"--------------onResume()"); super.onResume(); } @Override protectedvoidonPause(){ // TODO Auto-generated method stub Log.i(TAG,"B"+"--------------onPause()"); super.onPause(); } publicvoidfuncB(View?view){ startActivity(newIntent("com.feeyan.www.a_activity")); finish(); } }


    a_layout.xml
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?xml?version="1.0"encoding="utf-8"?> <RelativeLayout?xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <Button android:id="@+id/button_a_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="50dp" android:onClick="funcA" android:text="@string/button_a_text" android:textSize="20sp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="154dp" android:text="@string/page_a_text" android:textSize="30sp"/> </RelativeLayout>


    b.layout
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?xml?version="1.0"encoding="utf-8"?> <RelativeLayout?xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <Button android:id="@+id/button_b_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="50dp" android:onClick="funcB" android:text="@string/button_b_text" android:textSize="20sp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/button_b_id" android:layout_centerHorizontal="true" android:layout_marginBottom="140dp" android:text="@string/page_b_text" android:textSize="30sp"/> </RelativeLayout>


    AndroidManifest.xml
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <?xml?version="1.0"encoding="utf-8"?> <manifest?xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.startapptest" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="21"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <activity android:name=".A" android:label="@string/app_name"> <intent-filter> <action?android:name="android.intent.action.MAIN"/> <category?android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> <action?android:name="com.feeyan.www.a_activity"> </action> <category?android:name="android.intent.category.DEFAULT"> </category> </intent-filter> </activity> <activity android:name=".B" android:label="@string/app_name"> <intent-filter> <action?android:name="com.feeyan.www.b_activity"> </action> <category?android:name="android.intent.category.DEFAULT"> </category> </intent-filter> </activity> </application> </manifest>

    當(dāng)點(diǎn)擊A中的按鈕時(shí),跳轉(zhuǎn)到B,先暫停A,A從前臺(tái)轉(zhuǎn)入到后臺(tái),開始執(zhí)行B的onCreate、onResume方法,B被調(diào)入到棧頂,B現(xiàn)在可見,日志為:

    1 2 3 4 5 I/StartAppTest(26256):A--------------onCreate() I/StartAppTest(26256):A--------------onResume() I/StartAppTest(26256):A--------------onPause() I/StartAppTest(26256):B--------------onCreate() I/StartAppTest(26256):B--------------onResume()

    啟動(dòng)一個(gè)Activity的標(biāo)志是開始執(zhí)行生命周期onCreate方法,轉(zhuǎn)入到后臺(tái)的標(biāo)志是onPause方法,正在運(yùn)行、可見的標(biāo)志是onResume方法,本文將從源碼著手,分析啟動(dòng)activity的過程。

    1. 在Launcher桌面點(diǎn)擊app圖標(biāo)啟動(dòng)入口Activity

    本文基于android5.1.1源碼,在Launcher主頁(yè)面當(dāng)點(diǎn)擊圖表時(shí),調(diào)用過程為:

    onClick—->……—->startActivitySafely—->startActivity(v, intent, tag)—->startActivity(intent, optsBundle);

    源碼:packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

    intent設(shè)置了FLAG_ACTIVITY_NEW_TASK標(biāo)志,表示開啟一個(gè)新任務(wù),在新任務(wù)中啟動(dòng)activity,本案例沒有特殊的動(dòng)畫設(shè)置,optsBundle為null。

    framework層客戶端

    過程1 ? ?frameworks\base\core\java\android\app\Activity.java

    startActivity所屬的對(duì)象是this,表示當(dāng)前啟動(dòng)類Launcher對(duì)象,下一步執(zhí)行到Activity的startActivity方法,過程為:

    startActivity(Intent intent, @Nullable Bundle options)

    —-> startActivityForResult(intent, -1)

    —-> startActivityForResult(intent, requestCode, null)

    注:由于源碼較長(zhǎng),本文不貼上全部源碼,只給出方法名稱、部分代碼以及源碼路徑

    action為字符串“com.feeyan.www.b_activity”,requestCode等于-1,如果>=0, B被啟動(dòng)后會(huì)返回到A中,且A中的onActivityResult()方法會(huì)被調(diào)用。即便是調(diào)用startActivity,還是會(huì)調(diào)到 startActivityForResult,只不過此時(shí)requestCode是-1了。

    mParent:如果不為空,表示當(dāng)前Activity有子類,本案例沒有子類,為空,進(jìn)程執(zhí)行到:

    1 2 3 4 Instrumentation.ActivityResult?ar= mInstrumentation.execStartActivity( this,mMainThread.getApplicationThread(),mToken,this, intent,requestCode,options);

    要搞懂源碼,最關(guān)鍵的就是弄清楚參數(shù)的具體含義,源碼中參數(shù)有時(shí)候多達(dá)十幾個(gè),如果不清楚參數(shù)的來龍去脈,無從分析。

    this:當(dāng)前進(jìn)程還是在Launcher所在的進(jìn)程,this就是Launcher類的一個(gè)對(duì)象。

    mMainThread.getApplicationThread():返回一個(gè)ApplicationThread對(duì)象類型,也是一個(gè)IBinder對(duì)象類型,,mMainThread是ActivityThread的一個(gè)對(duì)象,代表當(dāng)前Launcher主線程對(duì)象

    mToken:也是一個(gè)IBinder對(duì)象類型

    requestCode仍為-1,options為null

    過程2 ? ?frameworks\base\core\java\android\app\Instrumentation.java

    1 2 3 4 5 publicActivityResult?execStartActivity( Context?who,IBinder?contextThread,IBinder?token,Activity?target, Intent?intent,intrequestCode,Bundle?options){ ...... }

    this對(duì)象傳給execStartActivity,該函數(shù)的第一個(gè)形參who是Context類型,第4個(gè)形參target是Activity類型,其實(shí)際類型都是Launcher對(duì)象,只是名字起的不一樣,這就是一種共識(shí),代表著某種含義,讀者看到名字就能猜得著其用意。

    1 IApplicationThread?whoThread=(IApplicationThread)contextThread;

    contextThread既是IBinder對(duì)象,也是IApplicationThread對(duì)象,此處向上轉(zhuǎn)型為IApplicationThread對(duì)象,

    1 2 3 4 5 intresult=ActivityManagerNative.getDefault() .startActivity(whoThread,who.getBasePackageName(),intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token,target!=null?target.mEmbeddedID:null, requestCode,0,null,options);

    ActivityManagerNative實(shí)現(xiàn)了IActivityManager接口,調(diào)用getDefault方法最終返回ActivityManagerService的代理類ActivityManagerProxy的一個(gè)對(duì)象,于是,startActivity便轉(zhuǎn)入到ActivityManagerProxy對(duì)象中開始執(zhí)行。

    過程3 ? ?frameworks\base\core\java\android\app\ActivityManagerNative.java

    1 2 3 4 5 publicintstartActivity(IApplicationThread?caller,StringcallingPackage,Intent?intent, StringresolvedType,IBinder?resultTo,StringresultWho,intrequestCode, intstartFlags,ProfilerInfo?profilerInfo,Bundle?options)throwsRemoteException{ ...... }

    分析參數(shù)時(shí),結(jié)合實(shí)際參數(shù)來看,否則單獨(dú)看形參不能確定具體含義。

    caller:前面?zhèn)鬟^來的值,代表ApplicationThread對(duì)象

    callingPackage:由who.getBasePackageName()的值傳遞而來,who是Context對(duì)象,getBasePackageName()的實(shí)現(xiàn)在ContextImple中,返回當(dāng)前啟動(dòng)類的包名,就是Launcher的包名

    resolvedType:解析當(dāng)前發(fā)送的Intent的MIME數(shù)據(jù)類型,本案例沒有為intent設(shè)置type、data屬性,因此,intent.resolveTypeIfNeeded(who.getContentResolver())返回null

    resultTo:Ibinder對(duì)象,具體含義后面繼續(xù)看

    resultWho:由target != null ? target.mEmbeddedID : null得來,target是activity對(duì)象即啟動(dòng)類Launcher對(duì)象,不為空,該語句返回mEmbeddedID,一個(gè)id號(hào),這個(gè)值必須要從Launcher這個(gè)apk啟動(dòng)中獲得,在Launcher啟動(dòng)后,代表Launcher啟動(dòng)類的對(duì)象是一個(gè)ActivityClientRecord對(duì)象,該對(duì)象所屬的類路徑為:

    frameworks\base\core\java\android\app\ActivityThread.java

    該對(duì)象的scheduleLaunchActivity方法中,有一句:

    ActivityClientRecord r = new ActivityClientRecord();

    在ActivityClientRecord的構(gòu)造方法中會(huì)把embeddedID初始化為null,因此mEmbeddedID為空

    startFlags:整型值,已經(jīng)初始化為0,具體作用后面分析

    profilerInfo:為null,具體作用后面分析

    這些參數(shù)都會(huì)被打包到持久化類Parcel的對(duì)象data中,把data作為transact的參數(shù)進(jìn)行跨進(jìn)程傳遞:

    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);

    該方法通過binder通信機(jī)制會(huì)傳遞到ActivityManagerNative的onTransact方法,在onTransact方法中,根據(jù)發(fā)送命令START_ACTIVITY_TRANSACTION找到case處理語句,把data中的數(shù)據(jù)取出來賦給相應(yīng)的變量,繼續(xù)調(diào)用:

    1 2 intresult=startActivity(app,callingPackage,intent,resolvedType, resultTo,resultWho,requestCode,startFlags,profilerInfo,options);

    startActivity最終會(huì)調(diào)用到服務(wù)端ActivityManagerService中。此時(shí),進(jìn)程也從啟動(dòng)類Launcher所在的進(jìn)程切換到了服務(wù)端進(jìn)程。從ActivityManagerNative.getDefault().startActivity一直到ActivityManagerService的startActivity方法,主要由binder通信實(shí)現(xiàn),該過程相當(dāng)復(fù)雜,但binder通信不屬于本文重點(diǎn),而且binder機(jī)制貫穿于整個(gè)Android系統(tǒng)、內(nèi)核、驅(qū)動(dòng)部分,本文如再遇到binder通信機(jī)制,直接給出最終被調(diào)用的類及方法。

    在進(jìn)入到服務(wù)端之前,看看客戶端到底做了哪些工作?

    主要是獲得了一些必要的參數(shù):IApplicationThread對(duì)象、啟動(dòng)類包名、Intent的MIME數(shù)據(jù)類型、IApplicationToken.Stub類型對(duì)象resultTo等,除了這些,沒有其他特殊的操作了,其實(shí)最關(guān)鍵的操作還是在服務(wù)端進(jìn)行的,這就是為何本文一開始提到無論哪種啟動(dòng)方式,客戶端都是大同小異。

    framework層服務(wù)端

    過程4 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

    startActivity—->startActivityAsUser—->mStackSupervisor.startActivityMayWait

    1 2 3 mStackSupervisor.startActivityMayWait(caller,-1,callingPackage,intent, resolvedType,null,null,resultTo,resultWho,requestCode,startFlags, profilerInfo,null,null,options,userId,null,null);

    這幾步?jīng)]有太多的操作,獲得了一個(gè)用戶id,用來作一些檢測(cè)

    過程5 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

    1 2 3 4 5 6 7 8 finalintstartActivityMayWait(IApplicationThread?caller,intcallingUid, StringcallingPackage,Intent?intent,StringresolvedType, IVoiceInteractionSession?voiceSession,IVoiceInteractor?voiceInteractor, IBinder?resultTo,StringresultWho,intrequestCode,intstartFlags, ProfilerInfo?profilerInfo,WaitResult?outResult,Configuration?config, Bundle?options,intuserId,IActivityContainer?iContainer,TaskRecord?inTask){ ...... }

    先看多了哪些參數(shù):
    voiceSession:IVoiceInteractionSession對(duì)象類型,被初始化null。IVoiceInteractionSession本是一個(gè)aidl遠(yuǎn)程接口,定義了任務(wù)棧啟動(dòng)taskStarted、任務(wù)棧結(jié)束taskFinished等方法

    voiceInteractor:IVoiceInteractor對(duì)象類型,被初始化為null。IVoiceInteractor也是一個(gè)aidl遠(yuǎn)程接口

    outResult:WaitResult對(duì)象類型,被初始化為null。WaitResult是IActivityManager的內(nèi)部類,實(shí)現(xiàn)了Parcelable接口,主要用來保存啟動(dòng)Activity后返回的結(jié)果信息

    config:Configuration對(duì)象類型,被初始化為null。Configuration描述了所有設(shè)備相關(guān)的配置信息,比如,本地語言、屏幕大小、屏幕方向、輸入法模式,可以通過Resources的getConfiguration獲得改對(duì)象

    iContainer:IActivityContainer對(duì)象類型,被初始化為null。IActivityContainer也是一個(gè)aidl遠(yuǎn)程接口

    inTask:TaskRecord對(duì)象類型,被初始化為null。TaskRecord很重要,會(huì)經(jīng)常用到此類,描述一個(gè)任務(wù)棧,每個(gè)任務(wù)棧可以包含多個(gè)Activity對(duì)象,每個(gè)TaskRecord對(duì)象都有一個(gè)當(dāng)前棧ActivityStack的引用,每個(gè)棧可以對(duì)應(yīng)多個(gè)TaskRecord對(duì)象

    除了這些多余的參數(shù),其他參數(shù)都是從客戶端傳遞而來。

    1 booleancomponentSpecified=intent.getComponent()!=null;

    getComponent方法返回一個(gè)ComponentName對(duì)象,該對(duì)象表示通過intent要啟動(dòng)的組件類,本案例就對(duì)應(yīng)A這個(gè)Activity,ComponentName對(duì)象一般用包名和類名標(biāo)識(shí)一個(gè)組件,因此,componentSpecified為true

    1 intent=newIntent(intent);

    根據(jù)客戶端傳遞過來的Intent對(duì)象重新構(gòu)建一個(gè)Intent對(duì)象,這樣做是不要破壞客戶端傳遞來的Intent對(duì)象

    1 2 3 4 5 6 7 ActivityInfo?aInfo=resolveActivity(intent,resolvedType,startFlags, profilerInfo,userId); ActivityInfo?resolveActivity(Intent?intent,StringresolvedType,intstartFlags, ProfilerInfo?profilerInfo,intuserId){ ...... }

    resolveActivity方法開始解析Intent對(duì)象,返回intent對(duì)應(yīng)的目標(biāo)Activity類的ActivityInfo對(duì)象,ActivityInfo類專門用來描述AndroidManifest.xml中Activity、Receiver組件信息的,本案例返回的就是A這個(gè)類對(duì)應(yīng)的信息,ActivityInfo的成員變量name就是類名稱,packageName就是包名稱,對(duì)應(yīng)本案例分別為com.example.startapptest.A和com.example.startapptest

    1 2 3 4 5 6 7 8 if(callingUid>=0){ callingPid=-1; }elseif(caller==null){ callingPid=realCallingPid; callingUid=realCallingUid; }else{ callingPid=callingUid=-1; }

    callingUid傳過來時(shí)為-1,call又不為空,進(jìn)程執(zhí)行else字句callingPid = callingUid = -1;

    1 2 3 4 5 6 7 8 ActivityContainer?container=(ActivityContainer)iContainer; finalActivityStack?stack; if(container==null||container.mStack.isOnHomeDisplay()){ stack=getFocusedStack(); }else{ stack=container.mStack; }

    iContainer為空,那么container也為空,調(diào)用getFocusedStack獲得當(dāng)前正在前臺(tái)的棧,也就是Launcher所在的棧。

    1 2 3 4 if(aInfo!=null&& (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE)!=0){ ...... }

    aInfo雖然不為空,但aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE卻為0,因?yàn)闆]有設(shè)置這種屬性,因此跳過該if語句,開始執(zhí)行:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 intres=startActivityLocked(caller,intent,resolvedType,aInfo, voiceSession,voiceInteractor,resultTo,resultWho, requestCode,callingPid,callingUid,callingPackage, realCallingPid,realCallingUid,startFlags,options, componentSpecified,null,container,inTask); finalintstartActivityLocked(IApplicationThread?caller, Intent?intent,StringresolvedType,ActivityInfo?aInfo, IVoiceInteractionSession?voiceSession,IVoiceInteractor?voiceInteractor, IBinder?resultTo,StringresultWho,intrequestCode, intcallingPid,intcallingUid,StringcallingPackage, intrealCallingPid,intrealCallingUid,intstartFlags,Bundle?options, booleancomponentSpecified,ActivityRecord[]outActivity,ActivityContainer?container, TaskRecord?inTask){ ...... }

    callingPid:int型變量,看字面意思與pid相關(guān),具體含義后面再看

    callingUid:int型變量,看字面意思與uid相關(guān),具體含義后面再看

    realCallingPid:啟動(dòng)類所在進(jìn)程的pid,本案例是Launcher

    realCallingUid:啟動(dòng)類所在進(jìn)程的uid,本案例是Launcher

    componentSpecified:為true,表明intent對(duì)應(yīng)的目標(biāo)Activity類存在

    outActivity:ActivityRecord數(shù)組名稱,初始化為null,ActivityRecord是一個(gè)動(dòng)態(tài)生成的對(duì)象,代表Activity在歷史棧中的記錄,ActivityRecord包含了Activity所有信息。

    1 2 3 4 5 6 7 8 9 10 11 12 13 ProcessRecord?callerApp=null; if(caller!=null){ callerApp=mService.getRecordForAppLocked(caller); if(callerApp!=null){ callingPid=callerApp.pid; callingUid=callerApp.info.uid; }else{ Slog.w(TAG,"Unable to find app for caller "+caller +" (pid="+callingPid+") when starting: " +intent.toString()); err=ActivityManager.START_PERMISSION_DENIED; } }

    mService是ActivityManagerService對(duì)象,通過getRecordForAppLocked方法獲得啟動(dòng)類所在進(jìn)程的進(jìn)程記錄對(duì)象ProcessRecord。參數(shù)caller是IApplicationThread對(duì)象,前文提到過,實(shí)際是ApplicationThread對(duì)象,代表Launcher類的主線程,caller在ActivityManagerService和ActivityThread兩個(gè)進(jìn)程之間完成通信,現(xiàn)在終于明白了,為何在startActivity時(shí)會(huì)帶著這樣一個(gè)參數(shù):服務(wù)端通過該參數(shù)獲得客戶端進(jìn)程信息,該參數(shù)起到橋梁作用。

    callerApp不為空,分別獲得啟動(dòng)類進(jìn)程的pid和uid保存到callingPid、callingUid中,這個(gè)callingPid和之前的realCallingPid獲得的值一樣,都是Launcher進(jìn)程pid

    1 2 3 4 5 6 7 8 9 10 11 12 ActivityRecord?sourceRecord=null; ActivityRecord?resultRecord=null; if(resultTo!=null){ sourceRecord=isInAnyStackLocked(resultTo); if(DEBUG_RESULTS)Slog.v( TAG,"Will send result to "+resultTo+" "+sourceRecord); if(sourceRecord!=null){ if(requestCode>=0&&!sourceRecord.finishing){ resultRecord=sourceRecord; } } }

    定義了兩個(gè)ActivityRecord變量sourceRecord、resultRecord,用來對(duì)應(yīng)啟動(dòng)類和目標(biāo)類。上文提到,resultTo屬于IBinder對(duì)象,屬于啟動(dòng)方的對(duì)象。isInAnyStackLocked方法根據(jù)啟動(dòng)類的標(biāo)記resultTo對(duì)象在列表?xiàng)V姓页鰧?duì)應(yīng)的棧,再在棧頂找到Activity記錄保存到sourceRecord中。

    1 finalintlaunchFlags=intent.getFlags();

    上文提到,intent一開始在客戶端就被設(shè)置了FLAG_ACTIVITY_NEW_TASK標(biāo)志,getFlags方法便取出該標(biāo)志,保存到launchFlags變量中。

    1 2 3 ActivityRecordr=newActivityRecord(mService,callerApp,callingUid,callingPackage, intent,resolvedType,aInfo,mService.mConfiguration,resultRecord,resultWho, requestCode,componentSpecified,this,container,options);

    創(chuàng)建一個(gè)ActivityRecord對(duì)象,這個(gè)ActivityRecord對(duì)象具體有什么作用?看看參數(shù)具體含義

    前4個(gè)參數(shù)代表了啟動(dòng)類Launcher,第5~7參數(shù)(Intent,resolvedType, aInfo)代表了目標(biāo)類

    mService.mConfiguration表示系統(tǒng)配置,resultRecord代表目標(biāo)類,resultWho代表啟動(dòng)類的一個(gè)id號(hào),為空

    componentSpecified為true

    this:代表當(dāng)前ActivityStackSupervisor對(duì)象

    從參數(shù)來看,該類既包含啟動(dòng)類的屬性,又包含目標(biāo)類屬性,推測(cè)該類應(yīng)該用來表達(dá)目標(biāo)類,后面可以證明。

    startActivityLocked方法的作用:獲得啟動(dòng)類進(jìn)程信息、pid、uid,創(chuàng)建ActivityRecord類對(duì)象sourceRecord保存啟動(dòng)類信息,創(chuàng)建ActivityRecord對(duì)象r,暫時(shí)推測(cè)代表目標(biāo)類,具體含義后面分析。進(jìn)程繼續(xù)調(diào)用:

    1 2 err=startActivityUncheckedLocked(r,sourceRecord,voiceSession,voiceInteractor, startFlags,true,options,inTask);

    開始調(diào)用下一步操作,第一個(gè)參數(shù)就是剛才創(chuàng)建的ActivityRecord對(duì)象;第二個(gè)參數(shù)是啟動(dòng)類對(duì)象,不為空;

    1 2 3 4 5 finalintstartActivityUncheckedLocked(ActivityRecordr,ActivityRecord?sourceRecord, IVoiceInteractionSession?voiceSession,IVoiceInteractor?voiceInteractor,intstartFlags, booleandoResume,Bundle?options,TaskRecord?inTask){ ...... }


    1 2 finalIntent?intent=r.intent; finalintcallingUid=r.launchedFromUid;

    r.intent就是傳遞而來的intent對(duì)象,r.launchedFromUid就是啟動(dòng)類Launcher的uid

    1 2 3 finalbooleanlaunchSingleTop=r.launchMode==ActivityInfo.LAUNCH_SINGLE_TOP; finalbooleanlaunchSingleInstance=r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE; finalbooleanlaunchSingleTask=r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK;

    這三個(gè)變量代表目標(biāo)類的啟動(dòng)模式,本案例就是A的啟動(dòng)模式,沒有任何設(shè)置,默認(rèn)為Standard模式,因而這三個(gè)變量都是false

    1 mUserLeaving=(launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION)==0;

    FLAG_ACTIVITY_NO_USER_ACTION:當(dāng)啟動(dòng)目標(biāo)Activity時(shí)Intent設(shè)置了此標(biāo)志,前臺(tái)正在行的Activity在暫停之前(執(zhí)行onPaused方法)不會(huì)回調(diào)onUserLeaveHint方法。NO_USER_ACTION表示非用戶操作,如果設(shè)置了此標(biāo)志,表示非用戶行為時(shí)不會(huì)回調(diào)onUserLeaveHint。比如,鬧鐘響了、來電話了,這屬于非用戶操作,如果設(shè)置了此標(biāo)志,就不會(huì)回調(diào)onUserLeaveHint,相反,如果是用戶操作行為比如按下HOME按鍵,返回鍵等,就會(huì)回調(diào)onUserLeaveHint。本案例中發(fā)送給A的Intent沒有設(shè)置該標(biāo)志,mUserLeaving為true,表明不是非用戶操作行為。

    1 2 ActivityRecord?notTop= (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)!=0?r:null;

    Intent沒有設(shè)置FLAG_ACTIVITY_PREVIOUS_IS_TOP,notTop為空

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 if(sourceRecord!=null){ if(sourceRecord.finishing){ // If the source is finishing, we can't further count it as our source. ?This // is because the task it is associated with may now be empty and on its way out, // so we don't want to blindly throw it in to that task. ?Instead we will take // the NEW_TASK flow and try to find a task for it. But save the task information // so it can be used when creating the new task. if((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)==0){ Slog.w(TAG,"startActivity called from finishing "+sourceRecord +"; forcing "+"Intent.FLAG_ACTIVITY_NEW_TASK for: "+intent); launchFlags|=Intent.FLAG_ACTIVITY_NEW_TASK; newTaskInfo=sourceRecord.info; newTaskIntent=sourceRecord.task.intent; } sourceRecord=null; sourceStack=null; }else{ sourceStack=sourceRecord.task.stack; } }else{ sourceStack=null; }

    sourceRecord不為空,變量finishing為空,因?yàn)榇藭r(shí)啟動(dòng)類Launcher還在前臺(tái),沒有進(jìn)入到銷毀列表中,進(jìn)程執(zhí)行else語句,得到啟動(dòng)類所在的棧對(duì)象并保存到sourceStack中。

    1 2 3 4 5 6 7 8 9 if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0&& (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK)==0) ||launchSingleInstance||launchSingleTask){ // If bring to front is requested, and no result is requested and we have not // been given an explicit task to launch in to, and // we can find a task that was started with this same // component, then instead of launching bring that one to the front. if(inTask==null&&r.resultTo==null){ ......

    Intent沒有設(shè)置FLAG_ACTIVITY_MULTIPLE_TASK,resultTo和startActivityLocked參數(shù)中resultTo不是一個(gè)意思,前者是在startActivityLocked函數(shù)中創(chuàng)建的ActivityRecord對(duì)象resultRecord,代表目標(biāo)類一方,被初始化為空,而后者代表啟動(dòng)類一方,不能混淆。

    1 2 3 4 ActivityRecord?intentActivity=!launchSingleInstance? findTaskLocked(r):findActivityLocked(intent,r.info); if(intentActivity!=null){ ......

    啟動(dòng)A時(shí)沒有設(shè)置啟動(dòng)模式,采用是默認(rèn)的標(biāo)準(zhǔn)模式,因此launchSingleInstance為false,調(diào)用findTaskLocked(r)在當(dāng)前棧頂中查詢是否有目標(biāo)類,如果有,就返回該類,否則,返回空。因?yàn)槭状螁?dòng)A,因此棧中肯定沒有A,返回空保存到intentActivity變量中,這樣的話,if語句不成立。如果棧中有實(shí)例,再次啟動(dòng)時(shí)就會(huì)執(zhí)行這段代碼。

    1 2 3 4 5 6 7 8 9 if(r.packageName!=null){ ActivityStack?topStack=getFocusedStack(); ActivityRecord?top=topStack.topRunningNonDelayedActivityLocked(notTop); if(top!=null&&r.resultTo==null){ if(top.realActivity.equals(r.realActivity)&&top.userId==r.userId){ ...... }else{ ...... }

    目標(biāo)類包名肯定不為空,執(zhí)行if條件,getFocusedStack返回當(dāng)前棧,topRunningNonDelayedActivityLocked返回當(dāng)前ActivityRecord對(duì)象保存到top中,肯定不為空;top.realActivity表示啟動(dòng)類,r.realActivity表示目標(biāo)類,本案例前者是Launcher,后者是A,兩者肯定不相等,因此if語句不成立,跳過此段。如果成立的話,就會(huì)在當(dāng)前棧中找到已存在的實(shí)例繼續(xù)使用。

    既然當(dāng)前棧中沒有已存在實(shí)例,那么只能新創(chuàng)建一個(gè)任務(wù)棧,繼續(xù)看:

    1 2 3 4 5 6 7 8 booleannewTask=false; booleankeepCurTransition=false; TaskRecord?taskToAffiliate=launchTaskBehind&&sourceRecord!=null? sourceRecord.task:null; // Should this be considered a new task? if(r.resultTo==null&&inTask==null&&!addingToTask &&(launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0){ ......

    此if語句成立,launchTaskBehind為空,那么taskToAffiliate也為空

    1 2 newTask=true; targetStack=adjustStackFocus(r,newTask);

    newTask代表新建一個(gè)任務(wù)的標(biāo)志,設(shè)為true;adjustStackFocus獲得一個(gè)ActivityStack保存到targetStack變量作為目標(biāo)類的棧;

    1 2 3 4 5 6 7 8 9 if(reuseTask==null){ r.setTask(targetStack.createTaskRecord(getNextTaskId(), newTaskInfo!=null?newTaskInfo:r.info, newTaskIntent!=null?newTaskIntent:intent, voiceSession,voiceInteractor,!launchTaskBehind/* toTop */), taskToAffiliate); if(DEBUG_TASKS)Slog.v(TAG,"Starting new activity "+r+" in new task "+ r.task); }

    createTaskRecord創(chuàng)建TaskRecord對(duì)象并放到棧頂,然后再放到目標(biāo)類ActivityRecord的task變量中

    1 2 3 4 5 6 targetStack.mLastPausedActivity=null; targetStack.startActivityLocked(r,newTask,doResume,keepCurTransition,options); if(!launchTaskBehind){ // Don't set focus on an activity that's going to the back. mService.setFocusedActivityLocked(r,"startedActivity"); }

    調(diào)用startActivityLocked進(jìn)行下一步操作

    startActivityUncheckedLocked函數(shù)非常復(fù)雜,最關(guān)鍵的就是查詢是否有已存在的TaskRcord作為目標(biāo)類的任務(wù)棧,如果棧中有就復(fù)用,否則就創(chuàng)建一個(gè)新的TaskRcord對(duì)象作為目標(biāo)類的任務(wù)棧。該函數(shù)涉及到了FLAG標(biāo)志,啟動(dòng)模式的判斷等,其目的就是找到一個(gè)合適的任務(wù)棧,為何要找到這個(gè)棧,就是因?yàn)锳ctivity在執(zhí)行時(shí)以棧這個(gè)數(shù)據(jù)結(jié)構(gòu)來管理。

    過程6 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

    1 2 finalvoidstartActivityLocked(ActivityRecordr,booleannewTask, booleandoResume,booleankeepCurTransition,Bundle?options)

    第一個(gè)參數(shù)對(duì)應(yīng)目標(biāo)類對(duì)象記錄,newTask為true,表示新建了一個(gè)任務(wù)棧,doResume為true,keepCurTransition為false。

    1 2 TaskRecord?rTask=r.task; finalinttaskId=rTask.taskId;

    r.task就是在startActivityUncheckedLocked中創(chuàng)建的目標(biāo)類的RaskRecord對(duì)象,取出來保存到rRask變量中

    1 2 3 4 5 6 7 if(!r.mLaunchTaskBehind&&(taskForIdLocked(taskId)==null||newTask)){ // Last activity in task had been removed or ActivityManagerService is reusing task. // Insert or replace. // Might not even be in. insertTaskAtTop(rTask); mWindowManager.moveTaskToTop(taskId); }

    mLaunchTaskBehind在上文得知為空,taskForIdLocked在歷史棧中查詢是否含有id號(hào)為目標(biāo)類所在的棧id,如果有,表明目標(biāo)類之前已經(jīng)被創(chuàng)建過,現(xiàn)在開始復(fù)用該對(duì)象,屬于非首次啟動(dòng),否則為首次啟動(dòng)對(duì)象,本案例首次啟動(dòng)A,因此,此函數(shù)返回null;newTask傳遞過來為true,if語句成立,調(diào)用insertTaskAtTop函數(shù)把新創(chuàng)建的TaskRecord對(duì)象插入到列表mTaskHistory的尾部,也就是插入到歷史棧頂;

    1 2 3 if(doResume){ mStackSupervisor.resumeTopActivitiesLocked(this,r,options); }

    過程7 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

    1 2 booleanresumeTopActivitiesLocked(ActivityStack?targetStack,ActivityRecord?target, Bundle?targetOptions)

    this對(duì)象表示當(dāng)前對(duì)象ActivityStack,此ActivityStack是新建的對(duì)象,不是Launcher所在的ActivityStack,是在startActivityUncheckedLocked中的adjustStackFocus方法獲得的,目的就是把新創(chuàng)建的任務(wù)插入到該ActivityStack對(duì)象中,這個(gè)對(duì)象就代表了目標(biāo)類所屬的棧

    第二個(gè)參數(shù)r就表示目標(biāo)類對(duì)象記錄,第三個(gè)參數(shù)依然為null

    1 2 3 if(isFrontStack(targetStack)){ result=targetStack.resumeTopActivityLocked(target,targetOptions); }

    isFrontStack判斷新獲得的ActivityStack對(duì)象位于棧頂,判斷為真,執(zhí)行if語句,調(diào)用resumeTopActivityLocked(target, targetOptions)

    過程8 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

    1 2 3 finalbooleanresumeTopActivityLocked(ActivityRecord?prev,Bundle?options){ ...... }

    第一個(gè)參數(shù)prev表示目標(biāo)類ActivityRecord對(duì)象,第二個(gè)傳遞過來為空

    1 2 3 4 5 6 7 8 try{ // Protect against recursion. mStackSupervisor.inResumeTopActivity=true; ...... result=resumeTopActivityInnerLocked(prev,options); }finally{ mStackSupervisor.inResumeTopActivity=false; }

    繼續(xù)調(diào)用resumeTopActivityInnerLocked方法,再調(diào)用resumeTopActivityInnerLocked

    1 2 3 finalbooleanresumeTopActivityInnerLocked(ActivityRecord?prev,Bundle?options){ ...... }


    1 2 3 4 5 6 7 // Find the first activity that is not finishing. finalActivityRecord?next=topRunningActivityLocked(null); // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. finalbooleanuserLeaving=mStackSupervisor.mUserLeaving; mStackSupervisor.mUserLeaving=false;

    topRunningActivityLocked方法找到棧頂?shù)腁ctivityRecord對(duì)象,此處對(duì)應(yīng)著A

    mStackSupervisor.mUserLeaving的值在過程5中被設(shè)置為true,此處取出來賦值給userLeaving,表明是用戶操作行為(按下返回鍵,HOME按鍵等);無論是true還是false,此處還是再?gòu)?fù)位一下,重新設(shè)置為false

    1 2 3 4 5 booleandontWaitForPause=(next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING)!=0; booleanpausing=mStackSupervisor.pauseBackStacks(userLeaving,true,dontWaitForPause); if(mResumedActivity!=null){ pausing|=startPausingLocked(userLeaving,false,true,dontWaitForPause);?? ? ? }

    目標(biāo)類A沒有設(shè)置FLAG_RESUME_WHILE_PAUSING標(biāo)志,dontWaitForPause為false

    pauseBackStacks函數(shù)返回false賦給pausing變量,mResumedActivity表示當(dāng)前正在前臺(tái)運(yùn)行的Activity,就是Launcher,不為空,進(jìn)程調(diào)用startPausingLocked繼續(xù)執(zhí)行

    1 2 3 4 5 6 pausing|=startPausingLocked(userLeaving,false,true,dontWaitForPause); finalbooleanstartPausingLocked(booleanuserLeaving,booleanuiSleeping,booleanresuming, booleandontWait){ ...... }

    startPausingLocked開始暫停當(dāng)前Activity,如果成功,返回true,否則false

    4個(gè)參數(shù)分別為false,false,true,false

    1 2 3 4 ActivityRecord?prev=mResumedActivity; mResumedActivity=null; mPausingActivity=prev; mLastPausedActivity=prev;

    mResumedActivity代表Launcher,先賦值給prev再置空;prev賦值給mPausingActivity,表明即將要暫停的Activity是Launcher,mLastPausedActivity也賦值為prev,表示剛剛暫停的Activity是哪個(gè)

    1 2 3 4 5 6 if(prev.app!=null&&prev.app.thread!=null){ ...... prev.app.thread.schedulePauseActivity(prev.appToken,prev.finishing, userLeaving,prev.configChangeFlags,dontWait); ...... }

    prev.app表示Launcher進(jìn)程信息,不為空;prev.app.thread是一個(gè)IApplicationThread對(duì)象,對(duì)應(yīng)Launcher也不為空,進(jìn)程繼續(xù)調(diào)用schedulePauseActivity方法,此處是一個(gè)Binder進(jìn)程間通信,下一步調(diào)用到ApplicationThread對(duì)象的schedulePauseActivity方法中,ApplicationThread是ActivityThread內(nèi)部類


    過程9 ? ?frameworks\base\core\java\android\app\ActivityThread.java

    1 2 3 4 5 6 7 8 publicfinalvoidschedulePauseActivity(IBinder?token,booleanfinished, booleanuserLeaving,intconfigChanges,booleandontReport){ sendMessage( finished?H.PAUSE_ACTIVITY_FINISHING:H.PAUSE_ACTIVITY, token, (userLeaving?1:0)|(dontReport?2:0), configChanges); }

    finished傳遞過來為false,因?yàn)長(zhǎng)auncher此時(shí)還沒有執(zhí)行生命周期方法onPause()、onDestory(),因此沒有進(jìn)入finishing狀態(tài),那么,sendMessage的第一個(gè)參數(shù)值為H.PAUSE_ACTIVITY

    sendMessage把消息發(fā)送到隊(duì)列中等待執(zhí)行,執(zhí)行方法是Handler的handleMessage方法,通過命令PAUSE_ACTIVITY可以得到執(zhí)行程序:

    1 2 3 4 5 6 7 casePAUSE_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"activityPause"); handlePauseActivity((IBinder)msg.obj,false,(msg.arg1&1)!=0,msg.arg2, (msg.arg1&2)!=0); maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;

    繼續(xù)調(diào)用handlePauseActivity方法

    1 2 3 4 privatevoidhandlePauseActivity(IBinder?token,booleanfinished, booleanuserLeaving,intconfigChanges,booleandontReport){ ...... }

    finished為false,userLeaving傳遞過來為true,configChanges為0,dontReport為false

    1 ActivityClientRecordr=mActivities.get(token);

    token對(duì)應(yīng)啟動(dòng)類Launcher,此處獲得Launcher的ActivityRecord對(duì)象

    1 2 3 if(userLeaving){ performUserLeavingActivity(r); }

    調(diào)用performUserLeavingActivity方法,performUserLeavingActivity的最終調(diào)用過程為:

    performUserLeavingActivity—->

    mInstrumentation.callActivityOnUserLeaving(r.activity) —->

    activity.performUserLeaving() —->

    onUserInteraction()

    onUserLeaveHint()

    意味著,如果是用戶操作的主動(dòng)行為,比如返回按鍵,遙控器上下左右按鍵,HOME按鍵燈,會(huì)調(diào)用Activity的

    onUserInteraction和onUserLeaveHint方法,如果是按鍵,觸摸、軌跡球被分發(fā)到Activity時(shí),onUserInteraction會(huì)被回調(diào);onUserLeaveHint的作用是當(dāng)Activity即將進(jìn)入到后臺(tái)前被回調(diào),起到提示作用

    performUserLeavingActivity執(zhí)行完后,進(jìn)程繼續(xù)調(diào)用

    1 2 3 4 5 6 7 8 performPauseActivity(token,finished,r.isPreHoneycomb()); finalBundle?performPauseActivity(ActivityClientRecordr,booleanfinished, booleansaveState){ ..... mInstrumentation.callActivityOnPause(r.activity); r.paused=true; }

    performPauseActivity方法中繼續(xù)調(diào)用callActivityOnPause方法,參數(shù)r.activity代表啟動(dòng)類Launcher


    過程9.1 ? ?frameworks\base\core\java\android\app\Instrumentation.java

    1 2 3 publicvoidcallActivityOnPause(Activity?activity){ activity.performPause(); }


    過程9.1.1 ? ?frameworks\base\core\java\android\app\Activity.java

    1 2 3 4 5 6 7 8 9 finalvoidperformPause(){ mDoReportFullyDrawn=false; mFragments.dispatchPause(); mCalled=false; onPause(); mResumed=false; ...... mResumed=false; }

    最終調(diào)用到Activity的performPause方法,再調(diào)用生命周期方法onPause()意味著啟動(dòng)類處于暫停狀態(tài)了,這一步執(zhí)行完后返回到performPauseActivity中,執(zhí)行r.paused = true把啟動(dòng)類的ActivityClientRecord的paused置為true,表示啟動(dòng)類此時(shí)已經(jīng)處于暫停狀態(tài)了。再返回到handlePauseActivity中,繼續(xù)執(zhí)行performPauseActivity后面的語句

    1 ActivityManagerNative.getDefault().activityPaused(token);

    這一步通過Binder進(jìn)程間通信機(jī)制進(jìn)入到ActivityManagerService的activityPaused方法中


    過程9.2 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

    1 2 3 4 5 publicfinalvoidactivityPaused(IBinder?token){ ...... stack.activityPausedLocked(token,false); ..... }


    過程10 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

    1 2 3 finalvoidactivityPausedLocked(IBinder?token,booleantimeout){ ...... }

    mPausingActivity表示啟動(dòng)類Launcher,r是Launcher的ActivityRecord對(duì)象,if條件為真,進(jìn)程繼續(xù)調(diào)用completePauseLocked(true)方法

    1 2 3 privatevoidcompletePauseLocked(booleanresumeNext){ ...... }

    既然啟動(dòng)類都已經(jīng)暫停了,那下一步工作是不是就是把目標(biāo)類啟動(dòng)起來呢?如果是的話,應(yīng)該會(huì)執(zhí)行生命周期onResume方法,這只是猜測(cè),具體詳細(xì)看方法的執(zhí)行過程

    參數(shù)resumeNext傳遞過來為true

    prev.finishing屬性為false,這個(gè)屬性一直沒有設(shè)置

    mPausingActivity = null;

    如果啟動(dòng)類已經(jīng)stop,就把mPausingActivity設(shè)為null

    進(jìn)程繼續(xù)執(zhí)行到:

    1 2 3 4 finalActivityStack?topStack=mStackSupervisor.getFocusedStack(); if(!mService.isSleepingOrShuttingDown()){ mStackSupervisor.resumeTopActivitiesLocked(topStack,prev,null); }

    當(dāng)前系統(tǒng)處于非睡眠和關(guān)機(jī)狀態(tài),if條件為真,進(jìn)程開始調(diào)用resumeTopActivitiesLocked方法


    過程11 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

    1 2 3 4 booleanresumeTopActivitiesLocked(ActivityStack?targetStack,ActivityRecord?target, Bundle?targetOptions){ ...... }

    要清楚方法具體做了什么,一定要先弄清楚參數(shù)的含義

    形參targetStack的實(shí)參是topStack,通過mStackSupervisor.getFocusedStack獲得,即當(dāng)前獲得焦點(diǎn)的棧,此處,啟動(dòng)類已經(jīng)暫停,那么當(dāng)前棧就是目標(biāo)類所在的棧,prev是啟動(dòng)類

    又調(diào)用了resumeTopActivityLocked方法

    1 2 3 if(isFrontStack(targetStack)){ result=targetStack.resumeTopActivityLocked(target,targetOptions); }


    過程12 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

    1 2 3 finalbooleanresumeTopActivityLocked(ActivityRecord?prev,Bundle?options){ ...... }

    繼續(xù)調(diào)用resumeTopActivityInnerLocked方法

    1 2 3 finalbooleanresumeTopActivityInnerLocked(ActivityRecord?prev,Bundle?options){ ...... }

    再次進(jìn)入到此方法時(shí),mResumedActivity為空,因?yàn)檫@是在過程8中startPausingLocked方法內(nèi)設(shè)置的,表明啟動(dòng)類Launcher已經(jīng)不在是當(dāng)前運(yùn)行的Activity,因此

    1 2 3 4 if(mResumedActivity!=null){ if(DEBUG_STATES)Slog.d(TAG,"resumeTopActivityLocked: Pausing "+mResumedActivity); pausing|=startPausingLocked(userLeaving,false,true,dontWaitForPause); }

    這個(gè)語句就不再成立,進(jìn)程跳過此句繼續(xù)執(zhí)行

    1 2 3 4 5 6 7 8 9 if(next.app!=null&&next.app.thread!=null){ ...... mStackSupervisor.startSpecificActivityLocked(next,true,false); ...... }else{ ...... mStackSupervisor.startSpecificActivityLocked(next,true,false); ...... }

    next就是目標(biāo)類A,此時(shí)A的一些棧等信息已經(jīng)構(gòu)建,但是A得進(jìn)程還沒有創(chuàng)建,正常情況下,啟動(dòng)一個(gè)新的應(yīng)用程序一般會(huì)創(chuàng)建一個(gè)新的進(jìn)程,應(yīng)用程序在此進(jìn)程中執(zhí)行,特殊情況下可以通過AndroidManifest中process屬性執(zhí)行指定應(yīng)用程序在某個(gè)進(jìn)程中執(zhí)行,本文沒有設(shè)置process屬性,默認(rèn)為啟動(dòng)一個(gè)新的進(jìn)程,本文后面會(huì)分析到。由此可知,A還沒有進(jìn)程信息,if語句不成立,進(jìn)程轉(zhuǎn)到else語句執(zhí)行


    過程13 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

    1 2 3 4 voidstartSpecificActivityLocked(ActivityRecordr, booleanandResume,booleancheckConfig){ ...... }


    1 2 3 4 5 6 7 8 9 ProcessRecord?app=mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid,true); if(app!=null&&app.thread!=null){ ...... realStartActivityLocked(r,app,andResume,checkConfig); ...... } mService.startProcessLocked(r.processName,r.info.applicationInfo,true,0, "activity",r.intent.getComponent(),false,false,true);

    此時(shí)A進(jìn)程還沒有創(chuàng)建,所以app為空,跳過if語句,開始調(diào)用startProcessLocked方法

    假如A的應(yīng)用程序已經(jīng)啟動(dòng),然后在A中啟動(dòng)B,B是A應(yīng)用程序的一個(gè)Activity,那么此時(shí)進(jìn)程已經(jīng)創(chuàng)建,app就不會(huì)為空,進(jìn)程會(huì)調(diào)用realStartActivityLocked方法


    過程14 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

    1 2 3 4 5 6 7 8 9 finalProcessRecord?startProcessLocked(StringprocessName, ApplicationInfo?info,booleanknownToBeDead,intintentFlags, StringhostingType,ComponentName?hostingName,booleanallowWhileBooting, booleanisolated,booleankeepIfLarge){ returnstartProcessLocked(processName,info,knownToBeDead,intentFlags,hostingType, hostingName,allowWhileBooting,isolated,0/* isolatedUid */,keepIfLarge, null/* ABI override */,null/* entryPoint */,null/* entryPointArgs */, null/* crashHandler */); }


    1 2 3 4 5 6 7 8 9 finalProcessRecord?startProcessLocked(StringprocessName,ApplicationInfo?info, booleanknownToBeDead,intintentFlags,StringhostingType,ComponentName?hostingName, booleanallowWhileBooting,booleanisolated,intisolatedUid,booleankeepIfLarge, StringabiOverride,StringentryPoint,String[]entryPointArgs,Runnable?crashHandler){ ...... }

    該方法中會(huì)為A創(chuàng)建ProcessRecord信息,然后繼續(xù)調(diào)用

    1 2 startProcessLocked( app,hostingType,hostingNameStr,abiOverride,entryPoint,entryPointArgs);


    1 2 3 4 5 6 7 8 9 privatefinalvoidstartProcessLocked(ProcessRecord?app,StringhostingType, StringhostingNameStr,StringabiOverride,StringentryPoint,String[]entryPointArgs){ ...... Process.ProcessStartResult?startResult=Process.start(entryPoint, app.processName,uid,uid,gids,debugFlags,mountExternal, app.info.targetSdkVersion,app.info.seinfo,requiredAbi,instructionSet, app.info.dataDir,entryPointArgs); ...... }

    此方法中會(huì)調(diào)用進(jìn)程的start方法創(chuàng)建一個(gè)新的進(jìn)程,具體是通過zygote進(jìn)程的來fork一個(gè)新的進(jìn)程,成為子進(jìn)程,子進(jìn)程共享父進(jìn)程資源,幾乎和父進(jìn)程一樣。當(dāng)子進(jìn)程創(chuàng)建好后,系統(tǒng)會(huì)分配一個(gè)進(jìn)程號(hào)PID給新進(jìn)程并返回,否則拋出異常

    1 2 3 4 5 6 7 8 9 10 11 12 publicstaticfinalProcessStartResult?start(finalStringprocessClass, finalStringniceName, intuid,intgid,int[]gids, intdebugFlags,intmountExternal, inttargetSdkVersion, StringseInfo, Stringabi, StringinstructionSet, StringappDataDir, String[]zygoteArgs){ ...... }

    第一個(gè)參數(shù)processClass為新創(chuàng)建的進(jìn)程的入口類即android.app.ActivityThread.java

    niceName:新創(chuàng)建的進(jìn)程的進(jìn)程名字,用ps命令可以查看到該名字,一般情況下,應(yīng)用程序的進(jìn)程名就是包名

    如果進(jìn)程創(chuàng)建成功,待方法start執(zhí)行完后,系統(tǒng)就會(huì)轉(zhuǎn)到ActivityThread.java的main方法入口開始執(zhí)行,注意這個(gè)流程,和我們通常看到的方法調(diào)用方法是不一樣的。


    過程15 ? ?frameworks\base\core\java\android\app\ActivityThread.java

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 publicstaticvoidmain(String[]args){ ...... Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread?thread=newActivityThread(); thread.attach(false); if(sMainThreadHandler==null){ sMainThreadHandler=thread.getHandler(); } if(false){ Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG,"ActivityThread")); } Looper.loop(); thrownewRuntimeException("Main thread loop unexpectedly exited"); }

    prepareMainLooper方法創(chuàng)建了looper對(duì)象和MessageQueue消息隊(duì)列;創(chuàng)建了并初始化ActivityThread對(duì)象,同時(shí)也創(chuàng)建并初始化了ApplicationThread對(duì)象mAppThread

    1 2 3 4 5 6 thread.attach(false); privatevoidattach(booleansystem){ ...... finalIActivityManager?mgr=ActivityManagerNative.getDefault(); mgr.attachApplication(mAppThread); }

    獲得ActivityManagerProxy對(duì)象,調(diào)用該對(duì)象的attachApplication方法,通過binder通信,最終調(diào)用到ActivityManagerService的attachApplication方法


    過程16 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

    1 2 3 4 5 publicfinalvoidattachApplication(IApplicationThread?thread){ synchronized(this){ ...... } }


    1 2 3 4 privatefinalbooleanattachApplicationLocked(IApplicationThread?thread, intpid){ ...... }

    先通過pid獲得ProcessRecord對(duì)象,該對(duì)象上一步創(chuàng)建過,不為空

    1 2 3 4 5 6 7 8 9 app.makeActive(thread,mProcessStats); app.curAdj=app.setAdj=-100; app.curSchedGroup=app.setSchedGroup=Process.THREAD_GROUP_DEFAULT; app.forcingToForeground=null; updateProcessForegroundLocked(app,false,false); app.hasShownUi=false; app.debugging=false; app.cached=false; app.killedByAm=false;

    初始化該P(yáng)rocessRecord對(duì)象

    1 2 3 4 5 6 7 8 9 10 11 // See if the top visible activity is waiting to run in this process... if(normalMode){ try{ if(mStackSupervisor.attachApplicationLocked(app)){ didSomething=true; } }catch(Exceptione){ Slog.wtf(TAG,"Exception thrown launching activities in "+app,e); badApp=true; } }

    這段話就是真正開始啟動(dòng)目標(biāo)Activity了,本案例就是A

    注:在這段話后面分別有:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // Find any services that should be running in this process... if(!badApp){ try{ Slog.i("zhulf","---------------------601"); didSomething|=mServices.attachApplicationLocked(app,processName); }catch(Exceptione){ Slog.wtf(TAG,"Exception thrown starting services in "+app,e); badApp=true; } } // Check if a next-broadcast receiver is in this process... if(!badApp&&isPendingBroadcastProcessLocked(pid)){ try{ didSomething|=sendPendingBroadcastsLocked(app); }catch(Exceptione){ // If the app died trying to launch the receiver we declare it 'bad' Slog.wtf(TAG,"Exception thrown dispatching broadcasts in "+app,e); badApp=true; } }

    用來啟動(dòng)Service、發(fā)送廣播,此處作為一個(gè)備注,如果要分析啟動(dòng)Service、廣播,研究這兩段語句,本文只研究啟動(dòng)Activity,因此,詳細(xì)看attachApplicationLocked方法


    過程17 ? ?frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

    1 2 3 booleanattachApplicationLocked(ProcessRecord?app)throwsRemoteException{ ...... }


    1 2 3 if(realStartActivityLocked(hr,app,true,true)){ didSomething=true; }


    1 2 3 4 5 finalbooleanrealStartActivityLocked(ActivityRecordr, ProcessRecord?app,booleanandResume,booleancheckConfig) throwsRemoteException{ ...... }

    hr就是目標(biāo)類ActivityRecord對(duì)象,app是進(jìn)程名字

    1 2 mService.updateLruProcessLocked(app,true,null); mService.updateOomAdjLocked();

    調(diào)整進(jìn)程LRU算法;參與管理進(jìn)程

    1 2 3 4 5 app.thread.scheduleLaunchActivity(newIntent(r.intent),r.appToken, System.identityHashCode(r),r.info,newConfiguration(mService.mConfiguration), r.compat,r.launchedFromPackage,r.task.voiceInteractor,app.repProcState, r.icicle,r.persistentState,results,newIntents,!andResume, mService.isNextTransitionForward(),profilerInfo);

    app.thrad是IApplicationThread對(duì)象,此處就是ApplicationThreadProxy對(duì)象接口,這里也是Binder通信過程,調(diào)用ApplicationThreadProxy的scheduleLaunchActivity方法,通過binder通信轉(zhuǎn)到ApplicationThread對(duì)象的scheduleLaunchActivity方法,該方法在ActivityThread對(duì)象中


    過程18 ? ?frameworks\base\core\java\android\app\ActivityThread.java

    1 2 3 4 5 6 7 8 publicfinalvoidscheduleLaunchActivity(Intent?intent,IBinder?token,intident, ActivityInfo?info,Configuration?curConfig,CompatibilityInfo?compatInfo, Stringreferrer,IVoiceInteractor?voiceInteractor,intprocState,Bundle?state, PersistableBundle?persistentState,List<ResultInfo>pendingResults, List<ReferrerIntent>pendingNewIntents,booleannotResumed,booleanisForward, ProfilerInfo?profilerInfo){ .... }

    該方法創(chuàng)建了目標(biāo)類對(duì)應(yīng)的ActivityClientRecord對(duì)象,而后初始化該對(duì)象,作為sendMessage參數(shù)發(fā)送到消息隊(duì)列待處理

    1 2 3 4 5 6 caseLAUNCH_ACTIVITY:{ finalActivityClientRecordr=(ActivityClientRecord)msg.obj; r.packageInfo=getPackageInfoNoCheck( r.activityInfo.applicationInfo,r.compatInfo); handleLaunchActivity(r,null); }


    1 2 3 privatevoidhandleLaunchActivity(ActivityClientRecordr,Intent?customIntent){ ...... }

    該方法分為兩個(gè)部分,先調(diào)用performLaunchActivity,再調(diào)用handleResumeActivity,最后還有finishActivity

    先看performLaunchActivity

    1 2 3 privateActivity?performLaunchActivity(ActivityClientRecordr,Intent?customIntent){ ...... }


    1 ActivityInfo?aInfo=r.activityInfo;

    從目標(biāo)類ActivityClientRecord對(duì)象中取出ActivityInfo對(duì)象,ActivityInfo對(duì)象包含了Activity、receiver對(duì)象信息

    1 ComponentName?component=r.intent.getComponent();

    再根據(jù)Intent獲得組件對(duì)象component,組件包含了啟動(dòng)類名稱和包名稱

    1 2 3 java.lang.ClassLoader?cl=r.packageInfo.getClassLoader(); activity=mInstrumentation.newActivity( cl,component.getClassName(),r.intent);

    通過Java反射機(jī)制找到目標(biāo)類文件,再創(chuàng)建目標(biāo)類的一個(gè)對(duì)象賦值給activity。從此處可知,原來Android中Activity對(duì)象是在啟動(dòng)時(shí)創(chuàng)建的,系統(tǒng)已經(jīng)幫助程序員寫好了new Activity對(duì)象的動(dòng)作,無需程序員自行new對(duì)象,這解決了一開始學(xué)習(xí)android時(shí)總是搞不清楚Activity對(duì)象是從什么時(shí)候創(chuàng)建的的困惑,因此,Android并不關(guān)注組件的創(chuàng)建過程,而把關(guān)注點(diǎn)落在了組件的生命周期上。

    1 Application?app=r.packageInfo.makeApplication(false,mInstrumentation);

    makeApplication方法也是利用反射機(jī)制找到應(yīng)用程序Application類并創(chuàng)建一個(gè)對(duì)象,并調(diào)用Application對(duì)象的onCreate方法,這就是為什么應(yīng)用一啟動(dòng)后,Application的onCreate比Activity的onCreate先執(zhí)行的原因!

    1 2 3 4 5 Context?appContext=createBaseContextForActivity(r,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);

    createBaseContextForActivity方法中會(huì)調(diào)用createActivityContext創(chuàng)建ContextImpl對(duì)象,ContextImpl實(shí)現(xiàn)了Context,也就同時(shí)創(chuàng)建了上下文管理者Context對(duì)象,這也解決了為什么在寫程序時(shí)總是能夠獲得Context對(duì)象的原因

    attach方法把Context對(duì)象、Instrumentation對(duì)象等和Activity關(guān)聯(lián)起來

    1 mInstrumentation.callActivityOnCreate(activity,r.state);

    通過Instrumentation對(duì)象的callActivityOnCreate方法進(jìn)入到Instrumentation對(duì)象中,Instrumentation對(duì)象就是監(jiān)控所有用戶和系統(tǒng)之間的交互操作,比如onCreate、onResume等

    過程19 ? ?frameworks\base\core\java\android\app\Instrumentation.java

    1 2 3 4 5 publicvoidcallActivityOnCreate(Activity?activity,Bundle?icicle){ prePerformCreate(activity); activity.performCreate(icicle); postPerformCreate(activity); }

    繼續(xù)調(diào)用performCreate方法

    過程20 ? ?frameworks\base\core\java\android\app\Activity.java

    1 2 3 4 5 finalvoidperformCreate(Bundle?icicle){ onCreate(icicle); mActivityTransitionState.readState(icicle); performCreateCommon(); }

    最終調(diào)用Activity的onCreate方法開始生命周期

    再返回到handleLaunchActivity中,進(jìn)程繼續(xù)執(zhí)行到handleResumeActivity方法

    1 2 3 4 ActivityClientRecordr=performResumeActivity(token,clearHide); r.activity.performResume(); mInstrumentation.callActivityOnResume(this); activity.onResume();

    這四步寫在一個(gè)代碼段,可以看到最終調(diào)用了Activity的onResume方法,目標(biāo)類A啟動(dòng)起來了并成為可見狀態(tài)

    到此處為止,在Launcher中啟動(dòng)Activity的過程就分析完了。

    整個(gè)過程相當(dāng)復(fù)雜,涉及到很多動(dòng)態(tài)對(duì)象,進(jìn)程間通信,棧的管理等,可以不必要理解每句代碼,但是清楚整個(gè)流程做了哪些核心的動(dòng)作是有必要的:

    1. ?過程5

    resolveActivity方法中調(diào)用了包管理器PackageManager的resolveIntent方法解析啟動(dòng)目標(biāo)類的Intent對(duì)象,獲得解析后的對(duì)象ActivityInfo,為何要獲得這個(gè)對(duì)象,這個(gè)對(duì)象有有什么作用?

    在AndroidManifest.xml中Activity和receiver標(biāo)簽包含了很多屬性,比如主題、啟動(dòng)模式、屏幕方向,輸入法設(shè)置,進(jìn)程名稱等,ActivityInfo就是對(duì)應(yīng)目標(biāo)類Activity的一個(gè)動(dòng)態(tài)對(duì)象,該對(duì)象包含了這些屬性信息。該對(duì)象的作用用來構(gòu)建目標(biāo)類對(duì)應(yīng)的ActivityRecord對(duì)象,該對(duì)象也是一個(gè)動(dòng)態(tài)對(duì)象,是歷史棧中的一條記錄,在內(nèi)存中對(duì)應(yīng)目標(biāo)類。

    這樣就明白一個(gè)問題:啟動(dòng)activity時(shí)有顯示和隱式,對(duì)于隱式方式,只需要在目標(biāo)類中intetn-filter中增加一個(gè)action,然后啟動(dòng)類通過這個(gè)action即可啟動(dòng)目標(biāo)類,這是如何做到的?實(shí)際上是通過包管理器PackageManager解析intent,查找到匹配的action對(duì)應(yīng)的目標(biāo)類的。

    2. 過程8

    startPausingLocked方法是進(jìn)入到暫停啟動(dòng)類過程的標(biāo)志,逐步調(diào)用prev.app.thread.schedulePauseActivity,然后又binder通信進(jìn)入到ApplicationThread對(duì)象的schedulePauseActivity方法,在此方法中,發(fā)送消息給啟動(dòng)類Launcher主線程ActivityThread,ActivityThread利用handler循環(huán)處理消息,調(diào)用handlePauseActivity方法處理,最終調(diào)用到Activity的生命周期方法onPause暫停啟動(dòng)類

    3. ?過程14

    Process對(duì)象的start方法開啟了一個(gè)新的進(jìn)程作為目標(biāo)類的主線程,由此,目標(biāo)類開始從ActivityThread的main方法開支執(zhí)行,然后由

    mgr.attachApplication(mAppThread);

    語句通過binder通信進(jìn)入到ActivityManagerService中,并傳遞了ApplicationThread對(duì)象,該對(duì)象傳入到ActivityManagerService中后構(gòu)建目標(biāo)類進(jìn)程信息,然后ActivityManagerService負(fù)責(zé)啟動(dòng)目標(biāo)類,最終通過該對(duì)象又通過Binder通信返回到ApplicationThread對(duì)象中,然后ApplicationThread對(duì)象又發(fā)送消息給目標(biāo)類主線程ActivityThread對(duì)象,該對(duì)象循環(huán)處理來自ActivityManagerService的消息,進(jìn)而調(diào)用Activity的生命周期方法onCreate、onResume。其中,IApplicationThread遠(yuǎn)程接口對(duì)象起著非常關(guān)鍵作用,他在主線程對(duì)象ActivityThread與Activity管理器ActivityManagerService對(duì)象之間起著通信橋梁作用。

    ActivityManagerService是Activity Manager(注意,分開大寫,代表Framework層的核心模塊,該模塊包含了ActivityManagerService、ActivityStack、Binder接口等)的核心部分,負(fù)責(zé)啟動(dòng)Activity、Service、發(fā)送Broadcast Receiver、啟動(dòng)ContentProvider;調(diào)整進(jìn)程調(diào)度算法,管理任務(wù)棧、檢查權(quán)限等一些列核心功能。


    2. 調(diào)用startActivity啟動(dòng)一個(gè)Activity

    在應(yīng)用程序內(nèi)啟動(dòng)Activity,和應(yīng)用程序外啟動(dòng)最根本的不同在于不會(huì)新創(chuàng)建進(jìn)程,也就是說,過程13中,不會(huì)執(zhí)行startProcessLocked方法,而執(zhí)行realStartActivityLocked方法,過程13~過程16可以省略不看,此時(shí)在同一個(gè)進(jìn)程中;除此之外,就是棧的獲取不一樣,用startActivity方法啟動(dòng)可能不會(huì)新建棧,直接使用已有的棧,而Launcher啟動(dòng)時(shí)一般會(huì)新建棧。

    3. 命令am start啟動(dòng)

    這種方法適合調(diào)試時(shí)使用,在串口中直接采用

    am start -n xxx/.yyy

    即可啟動(dòng)應(yīng)用程序,包名是xxx,入口類名是yyy

    am 這個(gè)可執(zhí)行程序?qū)?yīng)的源碼目錄位置:

    1 framework\base\cmds\am\src\com\android\commands\am\Am.java

    當(dāng)執(zhí)行這條命令時(shí),先從main方法開始執(zhí)行

    1 2 3 publicstaticvoidmain(String[]args){ (newAm()).run(args); }

    Am繼承了BaseCommand類,run方法在BaseCommand中定義,在run方法中又調(diào)用了onRun()方法,系統(tǒng)運(yùn)行時(shí)具體類型是Am,這樣就調(diào)到了Am的onRun方法。onRun中先執(zhí)行:

    1 mAm=ActivityManagerNative.getDefault();

    獲得ActivityManagerService的代理類ActivityManagerProxy的一個(gè)對(duì)象

    1 2 3 4 Stringop=nextArgRequired(); if(op.equals("start")){ runStart(); }

    判斷參數(shù)是否有start這個(gè)字符串,如果有,就調(diào)用runStart方法,本文啟動(dòng)Activity當(dāng)然含有start參數(shù)

    在runStart方法中,會(huì)執(zhí)行這一句

    1 2 res=mAm.startActivityAsUser(null,null,intent,mimeType, null,null,0,mStartFlags,profilerInfo,null,mUserId);

    這一步會(huì)繼續(xù)調(diào)用ActivityStackSupervisor的startActivityMayWait方法,與上文的“過程4”一樣,后面的步驟基本上差不多。

    不管哪種方式啟動(dòng),在服務(wù)端的操作基本上相同,區(qū)別就在于是否復(fù)用當(dāng)前棧還是新創(chuàng)建一個(gè)棧,是否新建一個(gè)進(jìn)程作為目標(biāo)類的進(jìn)程等;在客戶端,區(qū)別在于啟動(dòng)Activity的方式不同,第三種命令啟動(dòng)方式缺少了startActivity部分。

    http://blogread.cn/it/article/8026?

    總結(jié)

    以上是生活随笔為你收集整理的启动Activity的流程(Launcher中点击图标启动)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。