<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                startActivityLocked是startActivityMayWait第二階段的工作重點,該函數有點長,請讀者耐心看代碼。 **ActivityStack.java::startActivityLocked** ~~~ final int startActivityLocked(IApplicationThreadcaller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, ActivityInfo aInfo, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, boolean onlyIfNeeded, boolean componentSpecified, ActivityRecord[]outActivity) { int err = START_SUCCESS; ProcessRecord callerApp = null; //如果caller不為空,則需要從AMS中找到它的ProcessRecord。本例的caller為null if(caller != null) { callerApp = mService.getRecordForAppLocked(caller); //其實就是想得到調用進程的pid和uid if(callerApp != null) { callingPid = callerApp.pid;//一定要保證調用進程的pid和uid正確 callingUid = callerApp.info.uid; }else {//如調用進程沒有在AMS中注冊,則認為其是非法的 err = START_PERMISSION_DENIED; } }// if (caller != null)判斷結束 //下面兩個變量意義很重要。sourceRecord用于描述啟動目標Activity的那個Activity, //resultRecord用于描述接收啟動結果的Activity,即該Activity的onActivityResult //將被調用以通知啟動結果,讀者可先閱讀SDK中startActivityForResult函數的說明 ActivityRecordsourceRecord = null; ActivityRecord resultRecord = null; if(resultTo != null) { //本例resultTo為null, } //獲取Intent設置的啟動標志,它們是和Launch Mode相類似的“小把戲”, //所以,讀者務必理解“關于Launch Mode的介紹”一節的知識點 intlaunchFlags = intent.getFlags(); if((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) { ...... /* 前面介紹的Launch Mode和Activity的啟動有關,實際上還有一部分標志用于控制 Activity啟動結果的通知。有關FLAG_ACTIVITY_FORWARD_RESULT的作用,讀者可 參考SDK中的說明。使用這個標簽有個前提,即A必須先存在,正如if中sourceRecord 不為null的判斷所示。另外,讀者自己可嘗試編寫例子,以測試FLAG_ACTIVITY_FORWARD_ RESULT標志的作用 */ } //檢查err值及Intent的情況 if (err== START_SUCCESS && intent.getComponent() == null) err = START_INTENT_NOT_RESOLVED; ...... //如果err不為0,則調用sendActivityResultLocked返回錯誤 if (err!= START_SUCCESS) { if(resultRecord != null) {// resultRecord接收啟動結果 sendActivityResultLocked(-1,esultRecord, resultWho, requestCode, Activity.RESULT_CANCELED, null); } ....... returnerr; } //檢查權限 finalint perm = mService.checkComponentPermission(aInfo.permission, callingPid,callingUid,aInfo.applicationInfo.uid, aInfo.exported); ......//權限檢查失敗的處理,不必理會 if (mMainStack) { //可為AMS設置一個IActivityController類型的監聽者,AMS有任何動靜都會回調該 //監聽者。不過誰又有如此本領去監聽AMS呢?在進行Monkey測試的時候,Monkey會 //設置該回調對象。這樣,Monkey就能根據AMS放映的情況進行相應處理了 if(mService.mController != null) { boolean abort = false; try { Intent watchIntent = intent.cloneFilter(); //交給回調對象處理,由它判斷是否能繼續后面的行程 abort = !mService.mController.activityStarting(watchIntent, aInfo.applicationInfo.packageName); }...... //回調對象決定不啟動該Activity。在進行Monkey測試時,可設置黑名單,位于 //黑名單中的Activity將不能啟動 if (abort) { .......//通知resultRecord return START_SUCCESS; } } }// if(mMainStack)判斷結束 //創建一個ActivityRecord對象 ActivityRecordr = new ActivityRecord(mService, this, callerApp, callingUid, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified); if(outActivity != null) outActivity[0] = r;//保存到輸入參數outActivity數組中 if(mMainStack) { //mResumedActivity代表當前界面顯示的Activity if(mResumedActivity == null || mResumedActivity.info.applicationInfo.uid!= callingUid) { //檢查調用進程是否有權限切換Application,相關知識見下文的解釋 if(!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { PendingActivityLaunch pal = new PendingActivityLaunch(); //如果調用進程沒有權限切換Activity,則只能把這次Activity啟動請求保存起來, //后續有機會時再啟動它 pal.r = r; pal.sourceRecord = sourceRecord; ...... //所有Pending的請求均保存到AMS mPendingActivityLaunches變量中 mService.mPendingActivityLaunches.add(pal); mDismissKeyguardOnNextActivity = false; return START_SWITCHES_CANCELED; } }//if(mResumedActivity == null...)判斷結束 if (mService.mDidAppSwitch) {//用于控制app switch,見下文解釋 mService.mAppSwitchesAllowedTime = 0; } else{ mService.mDidAppSwitch = true; } //啟動處于Pending狀態的Activity mService.doPendingActivityLaunchesLocked(false); }// if(mMainStack)判斷結束 //調用startActivityUncheckedLocked函數 err =startActivityUncheckedLocked(r, sourceRecord, grantedUriPermissions, grantedMode, onlyIfNeeded, true); ...... return err; } ~~~ startActivityLocked函數的主要工作包括: - 處理sourceRecord及resultRecord。其中,sourceRecord表示發起本次請求的Activity,resultRecord表示接收處理結果的Activity(啟動一個Activity肯定需要它完成某項事情,當目標Activity將事情成后,就需要告知請求者該事情的處理結果)。在一般情況下,sourceRecord和resultRecord應指向同一個Activity。 - 處理app Switch。如果AMS當前禁止app switch,則只能把本次啟動請求保存起來,以待允許app switch時再處理。從代碼中可知,AMS在處理本次請求前,會先調用doPendingActivityLaunchesLocked函數,在該函數內部將啟動之前因系統禁止app switch而保存的Pending請求。 - 調用startActivityUncheckedLocked處理本次Activity啟動請求。 先來看app Switch,它雖然是一個小變量,但是意義重大。 1. 關于resume/stopAppSwitches的介紹 AMS提供了兩個函數,用于暫時(注意,是暫時)禁止App切換。為什么會有這種需求呢?因為當某些重要(例如設置賬號等)Activity處于前臺(即用戶當前所見的Activity)時,不希望系統因用戶操作之外的原因而切換Activity(例如恰好此時收到來電信號而彈出來電界面)。 先來看stopAppSwitches,代碼如下: **ActivityManagerService.java::stopAppSwitches** ~~~ public void stopAppSwitches() { ......//檢查調用進程是否有STOP_APP_SWITCHES權限 synchronized(this) { //設置一個超時時間,過了該時間,AMS可以重新切換App(switch app)了 mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME; mDidAppSwitch = false;//設置mDidAppSwitch為false mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG); Message msg =//防止應用進程調用了stop卻沒調用resume,5秒后處理該消息 mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG); mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME); } } ~~~ 在以上代碼中有兩點需要注意: - 此處控制機制名叫app switch,而不是activity switch。為什么呢?因為如果從受保護的activity中啟動另一個activity,那么這個新activity的目的應該是針對同一任務,這次啟動就不應該受app switch的制約,反而應該對其大開綠燈。目前,在執行Settings中設置設備策略(DevicePolicy)時就會stopAppSwitch。 - 執行stopAppSwitch后,應用程序應該調resumeAppSwitches以允許app switch,但是為了防止應用程序有意或無意忘記resume app switch,系統設置了一個超時(5秒)消息,過了此超時時間,系統將處理相應的消息,其內部會resume app switch。 再來看resumeAppSwitches函數,代碼如下: **ActivityManagerService::resumeAppSwitches** ~~~ public voidresumeAppSwitches() { ......//檢查調用進程是否有STOP_APP_SWITCHES權限 synchronized(this) { mAppSwitchesAllowedTime = 0; } //注意,系統并不在此函數內啟動那些被阻止的Activity } ~~~ 在resumeAppSwitches中只設置mAppSwitchesAllowedTime的值為0,它并不處理在stop和resume這段時間內積攢起的Pending請求,那么這些請求是在何時被處理的呢? - 從前面代碼可知,如果在執行resume app switch后,又有新的請求需要處理,則先處理那些pending的請求(調用doPendingActivityLaunchesLocked)。 - 在resumeAppSwitches中并未撤銷stopAppSwitches函數中設置的超時消息,所以在處理那條超時消息的過程中,也會處理pending的請求。 在本例中,由于不考慮app switch的情況,那么接下來的工作就是調用startActivityUncheckedLocked函數來處理本次activity的啟動請求。此時,我們已經創建了一個ActivityRecord用于保存目標Activity的相關信息。 2. startActivityUncheckedLocked函數分析 startActivityUncheckedLocked函數很長,但是目的比較簡單,即為新創建的ActivityRecord找到一個合適的Task。雖然本例最終的結果是創建一個新的Task,但是該函數的處理邏輯卻比較復雜。先看第一段分析。 (1) startActivityUncheckedLocked分析之一 **ActivityStack.java::startActivityUncheckedLocked** ~~~ final intstartActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, Uri[] grantedUriPermissions, intgrantedMode, boolean onlyIfNeeded, boolean doResume) { //在本例中,sourceRecord為null,onlyIfNeeded為false,doResume為true finalIntent intent = r.intent; final intcallingUid = r.launchedFromUid; intlaunchFlags = intent.getFlags(); //判斷是否需要調用因本次Activity啟動而被系統移到后臺的當前Activity的 //onUserLeaveHint函數。可閱讀SDK文檔中關于Activity onUserLeaveHint函數的說明 mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) ==0; //設置ActivityRecord的delayedResume為true,本例中的doResume為true if (!doResume) r.delayedResume = true; //在本例中,notTop為null ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; if(onlyIfNeeded) {....//在本例中,該變量為false,故略去相關代碼 } //根據sourceRecord的情況進行對應處理,能理解下面這段if/else的判斷語句嗎 if(sourceRecord == null) { //如果請求的發起者為空,則當然需要新建一個Task if((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; }else if (sourceRecord.launchMode ==ActivityInfo.LAUNCH_SINGLE_INSTANCE){ //如果sourceRecord單獨占一個Instance,則新的Activity必然處于另一個Task中 launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } else if(r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { //如果啟動模式設置了singleTask或singleInstance,則也要創建新Task launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; }//if(sourceRecord== null)判斷結束 //如果新Activity和接收結果的Activity不在一個Task中,則不能啟動新的Activity if(r.resultTo!= null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { sendActivityResultLocked(-1,r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); r.resultTo = null; } ~~~ startActivityUncheckedLocked第一階段的工作還算簡單,主要確定是否需要為新的Activity創建一個Task,即是否設置FLAG_ACTIVITY_NEW_TASK標志。 接下來看下一階段的工作。 (2) startActivityUncheckedLocked分析之二 **ActivityStack.java::startActivityUncheckedLocked** ~~~ booleanaddingToTask = false; TaskRecord reuseTask = null; if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK)== 0) || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { if(r.resultTo == null) { //搜索mHistory,得到一個ActivityRecord ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE ?findTaskLocked(intent, r.info) : findActivityLocked(intent,r.info); if (taskTop != null ){ ......//一堆復雜的邏輯處理,無非就是找到一個合適的Task,然后對應做一些 //處理。此處不討論這段代碼,讀者可根據工作中的具體情況進行研究 } }//if(r.resultTo == null)判斷結束 } ~~~ 在本例中,目標Activity首次登場,所以前面的邏輯處理都沒有起作用,建議讀者根據具體情況分析該段代碼。 下面來看startActivityUncheckLocked第三階段的工作。 (3) startActivityUncheckLocked分析之三 **ActivityStack.java::startActivityUncheckLocked** ~~~ if(r.packageName != null) { //判斷目標Activity是否已經在棧頂,如果是,需要判斷是創建一個新的Activity //還是調用onNewIntent(singleTop模式的處理) ActivityRecord top = topRunningNonDelayedActivityLocked(notTop); if (top != null && r.resultTo == null){ ......//不討論此段代碼 }//if(top != null...)結束 } else { ......//通知錯誤 returnSTART_CLASS_NOT_FOUND; } //在本例中,肯定需要創建一個Task booleannewTask = false; booleankeepCurTransition = false; if(r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { if(reuseTask == null) { mService.mCurTask++;//AMS中保存了當前Task的數量 if (mService.mCurTask <= 0) mService.mCurTask = 1; //為該AactivityRecord設置一個新的TaskRecord r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null,true); }else r.setTask(reuseTask, reuseTask,true); newTask = true; //下面這個函數為Android 4.0新增的,用于處理FLAG_ACTIVITY_TASK_ON_HOME的情況, //請閱讀SDK文檔對Intent的相關說明 moveHomeToFrontFromLaunchLocked(launchFlags); }elseif......//其他處理情況 //授權控制。在SDK中啟動Activity的函數沒有授權設置方面的參數。在實際工作中,筆者曾碰 //到過一個有趣的情況:在開發的一款定制系統中,用瀏覽器下載了受DRM保護的圖片, //此時要啟動Gallery3D來查看該圖片,但是由于為DRM目錄設置了讀寫權限,而Gallery3D //并未聲明相關權限,結果拋出異常,導致不能瀏覽該圖片。由于startActivity等函數不能設置 //授權,最終只能修改Gallery3D并為其添加use-permissions項了 if(grantedUriPermissions != null && callingUid > 0) { for(int i=0; i<grantedUriPermissions.length; i++) { mService.grantUriPermissionLocked(callingUid, r.packageName, grantedUriPermissions[i],grantedMode, r.getUriPermissionsLocked()); } mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, intent, r.getUriPermissionsLocked()); //調用startActivityLocked,此時ActivityRecord和TaskRecord均創建完畢 startActivityLocked(r, newTask, doResume, keepCurTransition); return START_SUCCESS; }//startActivityUncheckLocked函數結束 ~~~ startActivityUncheckLocked的第三階段工作也比較復雜,不過針對本例,它將創建一個新的TaskRecord,并調用startActivityLocked函數進行處理。 下面我們轉戰startActivityLocked函數。 (4) startActivityLocked函數分析 **ActivityStack.java::startActivityLocked** ~~~ private final voidstartActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition) { final intNH = mHistory.size(); intaddPos = -1; if(!newTask){//如果不是新Task,則從mHistory中找到對應的ActivityRecord的位置 ...... } if(addPos < 0) addPos = NH; //否則加到mHistory數組的最后 mHistory.add(addPos,r); //設置ActivityRecord的inHistory變量為true,表示已經加到mHistory數組中了 r.putInHistory(); r.frontOfTask = newTask; if (NH> 0) { //判斷是否顯示Activity切換動畫之類的事情,需要與WindowManagerService交互 } //最終調用resumeTopActivityLocked if (doResume) resumeTopActivityLocked(null);//重點分析這個函數 } ~~~ 在以上列出的startActivityLocked函數中,略去了一部分邏輯處理,這部分內容和Activity之間的切換動畫有關(通過這些動畫,使切換過程看起來更加平滑和美觀,需和WMS交互)。 * * * * * **提示**:筆者認為,此處將Activity切換和動畫處理這兩個邏輯揉到一起并不合適,但是似乎也沒有更合適的地方來進行該工作了。讀者不妨自行研讀一下該段代碼以加深體會。 * * * * * (5) startActivityUncheckedLocked總結 說實話,startActivityUncheckedLocked函數的復雜度超乎筆者的想象,光這些函數名就夠讓人頭疼的。但是針對本例而言,相關邏輯的難度還算適中,畢竟這是Activity啟動流程中最簡單的情況。可用一句話總結本例中startActivityUncheckedLocked函數的功能:創建ActivityRecord和TaskRecord并將ActivityRecord添加到mHistory末尾,然后調用resumeTopActivityLocked啟動它。 下面用一節來分析resumeTopActivityLocked函數。 3. resumeTopActivityLocked函數分析 **ActivityStack.java::resumeTopActivityLocked** ~~~ finalboolean resumeTopActivityLocked(ActivityRecord prev) { //從mHistory中找到第一個需要啟動的ActivityRecord ActivityRecord next = topRunningActivityLocked(null); finalboolean userLeaving = mUserLeaving; mUserLeaving = false; if (next== null) { //如果mHistory中沒有要啟動的Activity,則啟動Home if(mMainStack) returnmService.startHomeActivityLocked(); } //在本例中,next將是目標Activity next.delayedResume= false; ......//和WMS交互,略去 //將該ActivityRecord從下面幾個隊列中移除 mStoppingActivities.remove(next); mGoingToSleepActivities.remove(next); next.sleeping = false; mWaitingVisibleActivities.remove(next); //如果當前正在中斷一個Activity,需先等待那個Activity pause完畢,然后系統會重新 //調用resumeTopActivityLocked函數以找到下一個要啟動的Activity if(mPausingActivity != null) return false; /************************請讀者注意***************************/ //①mResumedActivity指向上一次啟動的Activity,也就是當前界面顯示的這個Activity //在本例中,當前Activity就是Home界面 if(mResumedActivity != null) { //先中斷 Home。這種情況放到最后進行分析 startPausingLocked(userLeaving,false); return true; } //②如果mResumedActivity為空,則一定是系統第一個啟動的Activity,讀者應能猜測到它就 //是Home ......//如果prev不為空,則需要通知WMS進行與Activity切換相關的工作 try { //通知PKMS修改該Package stop狀態,詳細信息參考第4章“readLPw的‘佐料’” //一節的說明 AppGlobals.getPackageManager().setPackageStoppedState( next.packageName, false); }...... if(prev!= null){ ......//還是和WMS有關,通知它停止繪畫 } if(next.app != null && next.app.thread != null) { //如果該ActivityRecord已有對應的進程存在,則只需要重啟Activity。就本例而言, //此進程還不存在,所以要先創建一個應用進程 } else { //第一次啟動 if (!next.hasBeenLaunched) { next.hasBeenLaunched = true; } else { ......//通知WMS顯示啟動界面 } //調用另外一個startSpecificActivityLocked函數 startSpecificActivityLocked(next, true, true); } returntrue; } ~~~ resumeTopActivityLocked函數中有兩個非常重要的關鍵點: - 如果mResumedActivity不為空,則需要先暫停(pause)這個Activity。由代碼中的注釋可知,mResumedActivity代表上一次啟動的(即當前正顯示的)Activity。現在要啟動一個新的Activity,須先停止當前Activity,這部分工作由startPausingLocked函數完成。 - 那么,mResumedActivity什么時候為空呢?當然是在啟動全系統第一個Activity時,即啟動Home界面的時候。除此之外,該值都不會為空。 先分析第二個關鍵點,即mResumedActivity為null的情況選擇分析此種情況的原因是:如果先分析startPausingLocked,則后續分析會牽扯三個進程,即當前Activity所在進程、AMS所在進程及目標進程,分析的難度相當大。 好了,繼續我們的分析。resumeTopActivityLocked最后將調用另外一個startSpecificActivityLocked,該函數將真正創建一個應用進程。 (1) startSpecificActivityLocked分析 **ActivityStack.java::startSpecificActivityLocked** ~~~ private final voidstartSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { //從AMS中查詢是否已經存在滿足要求的進程(根據processName和uid來查找) //在本例中,查詢結果應該為null ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid); //設置啟動時間等信息 if(r.launchTime == 0) { r.launchTime = SystemClock.uptimeMillis(); if(mInitialStartTime == 0) mInitialStartTime = r.launchTime; } else if(mInitialStartTime == 0) { mInitialStartTime = SystemClock.uptimeMillis(); } //如果該進程存在并已經向AMS注冊(例如之前在該進程中啟動了其他Activity) if (app!= null && app.thread != null) { try { app.addPackage(r.info.packageName); //通知該進程中的啟動目標Activity realStartActivityLocked(r, app, andResume, checkConfig); return; }...... } //如果該進程不存在,則需要調用AMS的startProcessLocked創建一個應用進程 mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity",r.intent.getComponent(), false); } ~~~ 來看AMS的startProcessLocked函數,它將創建一個新的應用進程。 (2) startProcessLocked分析 **ActivityManagerService.java::startProcessLocked** ~~~ final ProcessRecord startProcessLocked(StringprocessName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting) { //根據processName和uid尋找是否已經存在ProcessRecord ProcessRecordapp = getProcessRecordLocked(processName, info.uid); if (app!= null && app.pid > 0) { ......//處理相關情況 } StringhostingNameStr = hostingName != null ? hostingName.flattenToShortString() : null; //①處理FLAG_FROM_BACKGROUND標志,見下文解釋 if((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) { if(mBadProcesses.get(info.processName, info.uid) != null) return null; } else { mProcessCrashTimes.remove(info.processName,info.uid); if(mBadProcesses.get(info.processName, info.uid) != null) { mBadProcesses.remove(info.processName, info.uid); if (app != null) app.bad =false; } } if (app== null) { //創建一個ProcessRecord,并保存到mProcessNames中。注意,此時還沒有創建實際進程 app= newProcessRecordLocked(null, info, processName); mProcessNames.put(processName, info.uid, app); }else app.addPackage(info.packageName); ...... //②調用另外一個startProcessLocked函數 startProcessLocked(app, hostingType, hostingNameStr); return(app.pid != 0) ? app : null; } ~~~ 在以上代碼中列出兩個關鍵點,其中第一點和FLAG_FROM_BACKGROUND有關,相關知識點如下: - FLAG_FROM_BACKGROUND標識發起這次啟動的Task屬于后臺任務。很顯然,手機中沒有界面供用戶操作位于后臺Task中的Activity。如果沒有設置該標志,那么這次啟動請求就是由前臺Task因某種原因而觸發的(例如用戶單擊某個按鈕)。 - 如果一個應用進程在1分鐘內連續崩潰超過2次,則AMS會將其ProcessRecord加入所謂的mBadProcesses中。一個應用崩潰后,系統會彈出一個警告框以提醒用戶。但是,如果一個后臺Task啟動了一個“BadProcess”,然后該Process崩潰,結果彈出一個警告框,那么用戶就會覺得很奇怪:“為什么突然彈出一個框?”因此,此處將禁止后臺Task啟動“Bad Process”。如果用戶主動選擇啟動(例如單擊一個按鈕),則不能禁止該操作,并且要把應用進程從mBadProcesses中移除,以給它們“重新做人”的機會。當然,要是該應用每次啟動時都會崩潰,而且用戶不停地去啟動,那該用戶可能是位測試工作者。 * * * * * **提示**:這其實是一種安全機制,防止不健全的程序不斷啟動可能會崩潰的組件,但是這種機制并不限制用戶的行為。 * * * * * 下面來看第二個關鍵點,即另一個startProcessLocked函數,其代碼如下: **ActivityManagerService.java::startProcessLocked** ~~~ private final voidstartProcessLocked(ProcessRecord app, String hostingType, StringhostingNameStr) { if(app.pid > 0 && app.pid != MY_PID) { synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } app.pid = 0; } //mProcessesOnHold用于保存那些在系統還沒有準備好就提前請求啟動的ProcessRecord mProcessesOnHold.remove(app); updateCpuStats(); System.arraycopy(mProcDeaths, 0, mProcDeaths, 1, mProcDeaths.length-1); mProcDeaths[0] = 0; try { intuid = app.info.uid; int[] gids = null; try {//從PKMS中查詢該進程所屬的gid gids = mContext.getPackageManager().getPackageGids( app.info.packageName); }...... ......//工廠測試 intdebugFlags = 0; if((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { debugFlags |=Zygote.DEBUG_ENABLE_DEBUGGER; debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; }......//設置其他一些debugFlags //發送消息給Zygote,它將派生一個子進程,該子進程執行ActivityThread的main函數 //注意,我們傳遞給Zygote的參數并沒有包含任何與Activity相關的信息。現在僅僅啟動 //一個應用進程 Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, app.info.targetSdkVersion, null); //電量統計項 BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); synchronized (bs) { if(bs.isOnBattery()) app.batteryStats.incStartsLocked(); } //如果該進程為persisitent,則需要通知Watchdog,實際上processStarted內部只 //關心剛才創建的進程是不是com.android.phone if(app.persistent) { Watchdog.getInstance().processStarted(app.processName, startResult.pid); } app.pid= startResult.pid; app.usingWrapper = startResult.usingWrapper; app.removed = false; synchronized (mPidsSelfLocked) { //以pid為key,將代表該進程的ProcessRecord對象加入到mPidsSelfLocked中保管 this.mPidsSelfLocked.put(startResult.pid, app); //發送一個超時消息,如果這個新創建的應用進程10秒內沒有和AMS交互,則可斷定 //該應用進程啟動失敗 Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); msg.obj = app; //正常的超時時間為10秒。不過如果該應用進程通過valgrind加載,則延長到300秒 //valgrind是Linux平臺上一款檢查內存泄露的程序,被加載的應用將在它的環境中工作, //這項工作需耗費較長時間。讀者可查詢valgrind的用法 mHandler.sendMessageDelayed(msg,startResult.usingWrapper ?PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); }...... } ~~~ startProcessLocked通過發送消息給Zygote以派生一個應用進程[^①],讀者仔細研究所發消息的內容,大概會發現此處并未設置和Activity相關的信息,也就是說,該進程啟動后,將完全不知道自己要干什么,怎么辦?下面就此進行分析。 4. startActivity分析之半程總結 很抱歉,我們現在還處于startActivity分析之旅的中間點,即使越過了很多險灘惡途,一路走來還是發覺有點艱難。此處用圖6-14來記錄半程中的各個關鍵點。 :-: ![](http://img.blog.csdn.net/20150803123218950?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖6-14 startActivity半程總結 圖6-14列出了針對本例的調用順序,其中對每個函數的大體功能也做了簡單描述。 * * * * * **注意**:圖6-14中的調用順序及功能說明只是針對本例而言的。讀者以后可結合具體情況再深入研究其中的內容。 * * * * * 5. 應用進程的創建及初始化 如前所述,應用進程的入口是ActivityThread的main函數,它是在主線程中執行的,其代碼如下: **ActivityThread.java::main** ~~~ public static void main(String[] args) { SamplingProfilerIntegration.start(); //和調試及strictMode有關 CloseGuard.setEnabled(false); //設置進程名為"<pre-initialized>" Process.setArgV0("<pre-initialized>"); //準備主線程消息循環 Looper.prepareMainLooper(); if(sMainThreadHandler == null) sMainThreadHandler = new Handler(); //創建一個ActivityThread對象 ActivityThread thread = new ActivityThread(); //①調用attach函數,注意其參數值為false thread.attach(false); Looper.loop(); //進入主線程消息循環 throw newRuntimeException("Main thread loop unexpectedly exited"); } ~~~ 在main函數內部將創建一個消息循環Loop,接著調用ActivityThread的attach函數,最終將主線程加入消息循環。 我們在分析AMS的setSystemProcess時曾分析過ActivityThread的attach函數,那時傳入的參數值為true。現在來看設置其為false的情況: **ActivityThread.java::attach** ~~~ private void attach(boolean system) { sThreadLocal.set(this); mSystemThread = system; if(!system) { ViewRootImpl.addFirstDrawHandler(new Runnable() { public void run() { ensureJitEnabled(); } }); //設置在DDMS中看到的本進程的名字為"<pre-initialized>" android.ddm.DdmHandleAppName.setAppName("<pre-initialized>"); //設置RuntimeInit的mApplicationObject參數,后續會介紹RuntimeInit類 RuntimeInit.setApplicationObject(mAppThread.asBinder()); //獲取和AMS交互的Binder客戶端 IActivityManager mgr = ActivityManagerNative.getDefault(); try { //①調用AMS的attachApplication,mAppThread為ApplicationThread類型, //它是應用進程和AMS交互的接口 mgr.attachApplication(mAppThread); }...... } else......// system process的處理 ViewRootImpl.addConfigCallback(newComponentCallbacks2() {.......//添加回調函數}); } ~~~ 我們知道,AMS創建一個應用進程后,會設置一個超時時間(一般是10秒)。如果超過這個時間,應用進程還沒有和AMS交互,則斷定該進程創建失敗。所以,應用進程啟動后,需要盡快和AMS交互,即調用AMS的attachApplication函數。在該函數內部將調用attachApplicationLocked,所以此處直接分析attachApplicationLocked,先看其第一階段的工作。 (1) attachApplicationLocked分析之一 **ActivityManagerService.java::attachApplicationLocked** ~~~ private final booleanattachApplicationLocked(IApplicationThread thread, int pid) {//此pid代表調用進程的pid ProcessRecord app; if (pid != MY_PID && pid >= 0) { synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid);//根據pid查找對應的ProcessRecord對象 } }else app = null; /* 如果該應用進程由AMS啟動,則它一定在AMS中有對應的ProcessRecord,讀者可回顧前面創建 應用進程的代碼:AMS先創建了一個ProcessRecord對象,然后才發命令給Zygote。 如果此處app為null,表示AMS沒有該進程的記錄,故需要“殺死”它 */ if (app== null) { if(pid > 0 && pid != MY_PID) //如果pid大于零,且不是SystemServer進程,則 //Quietly(即不打印任何輸出)”殺死”process Process.killProcessQuiet(pid); else{ //調用ApplicationThread的scheduleExit函數。應用進程完成處理工作后 //將退出運行 //為何不像上面一樣直接殺死它呢?可查閱linux pid相關的知識并自行解答 thread.scheduleExit(); } returnfalse; } /* 判斷app的thread是否為空,如果不為空,則表示該ProcessRecord對象還未和一個 應用進程綁定。注意,app是根據pid查找到的,如果舊進程沒有被殺死,系統則不會重用 該pid。為什么此處會出現ProcessRecord thread不為空的情況呢?見下面代碼的注釋說明 */ if(app.thread != null) handleAppDiedLocked(app, true, true); StringprocessName = app.processName; try { /* 創建一個應用進程訃告接收對象。當應用進程退出時,該對象的binderDied將被調 用。這樣,AMS就能做相應處理。binderDied函數將在另外一個線程中執行,其內部也會 調用handleAppDiedLocked。假如用戶在binderDied被調用之前又啟動一個進程, 那么就會出現以上代碼中app.thread不為null的情況。這是多線程環境中常出現的 情況,不熟悉多線程編程的讀者要仔細體會。 */ AppDeathRecipient adr = new AppDeathRecipient(pp, pid, thread); thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; }...... //設置該進程的調度優先級和oom_adj等成員 app.thread= thread; app.curAdj = app.setAdj = -100; app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.forcingToForeground = null; app.foregroundServices = false; app.hasShownUi = false; app.debugging = false; //啟動成功,從消息隊列中撤銷PROC_START_TIMEOUT_MSG消息 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); ~~~ attachApplicationLocked第一階段的工作比較簡單: - 設置代表該應用進程的ProcessRecrod對象的一些成員變量,例如用于和應用進程交互的thread對象、進程調度優先級及oom_adj的值等。 - 從消息隊列中撤銷PROC_START_TIMEOUT_MSG。 至此,該進程啟動成功,但是這一階段的工作僅針對進程本身(如設置調度優先級,oom_adj等),還沒有涉及和Activity啟動相關的內容,這部分工作將在第二階段完成。 (2) attachApplicationLocked分析之二 **ActivityManagerService.java::attachApplicationLocked** ~~~ ...... //SystemServer早就啟動完畢,所以normalMode為true booleannormalMode = mProcessesReady || isAllowedWhileBooting(app.info); /* 我們在6.2.3的標題1中分析過generateApplicationProvidersLocked函數, 在該函數內部將查詢(根據進程名,uid確定)PKMS以獲取需運行在該進程中的ContentProvider */ Listproviders = normalMode ? generateApplicationProvidersLocked(app) : null; try { int testMode = IApplicationThread.DEBUG_OFF; if(mDebugApp != null && mDebugApp.equals(processName)) { ......//處理debug選項 } ......//處理Profile boolean isRestrictedBackupMode = false; ......// //dex化對應的apk包 ensurePackageDexOpt(app.instrumentationInfo!= null ? app.instrumentationInfo.packageName : app.info.packageName); //如果設置了Instrumentation類,該類所在的Package也需要dex化 if(app.instrumentationClass != null) ensurePackageDexOpt(app.instrumentationClass.getPackageName()); ApplicationInfo appInfo =app.instrumentationInfo != null ? app.instrumentationInfo :app.info; //查詢該Application使用的CompatibiliyInfo app.compat =compatibilityInfoForPackageLocked(appInfo); if (profileFd != null) //用于記錄性能文件 profileFd = profileFd.dup(); //①通過ApplicationThread和應用進程交互,調用其bindApplication函數 thread.bindApplication(processName,appInfo, providers, app.instrumentationClass, profileFile, profileFd, profileAutoStop,app.instrumentationArguments, app.instrumentationWatcher,testMode, isRestrictedBackupMode || !normalMode, app.persistent, mConfiguration, app.compat, getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); //updateLruProcessLocked函數以后再作分析 updateLruProcessLocked(app,false, true); //記錄兩個時間 app.lastRequestedGc= app.lastLowMemory = SystemClock.uptimeMillis(); }......//try結束 ..//從mProcessesOnHold和mPersistentStartingProcesses中刪除相關信息 mPersistentStartingProcesses.remove(app); mProcessesOnHold.remove(app); ~~~ 由以上代碼可知,第二階段的工作主要是為調用ApplicationThread的bindApplication做準備,將在后面的章節中分析該函數的具體內容。此處先來看它的原型。 ~~~ /* 正如我們在前面分析時提到的,剛創建的這個進程并不知道自己的歷史使命是什么,甚至連自己的 進程名都不知道,只能設為"<pre-initialized>"。其實,Android應用進程的歷史使命是 AMS在其啟動后才賦予它的,這一點和我們理解的一般意義上的進程不太一樣。根據之前的介紹, Android的組件應該運行在Android運行環境中。從OS角度來說,該運行環境需要和一個進程綁定。 所以,創建應用進程這一步只是創建了一個能運行Android運行環境的容器,而我們的工作實際上 還遠未結束。 bindApplication的功能就是創建并初始化位于該進程中的Android運行環境 */ public final void bindApplication( StringprocessName,//進程名,一般是package名 ApplicationInfo appInfo,//該進程對應的ApplicationInfo List<ProviderInfo> providers,//在該APackage中聲明的Provider信息 ComponentName instrumentationName,//和instrumentation有關 //下面3個參數和性能統計有關 StringprofileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler, //這兩個和Instrumentation有關,在本例中,這幾個參數暫時都沒有作用 Bundle instrumentationArgs, IInstrumentationWatcherinstrumentationWatcher, intdebugMode,//調試模式 boolean isRestrictedBackupMode, boolean persistent,//該進程是否是persist Configuration config,//當前的配置信息,如屏幕大小和語言等 CompatibilityInfocompatInfo,//兼容信息 //AMS將常用的Service信息傳遞給應用進程,目前傳遞的Service信息只有PKMS、 //WMS及AlarmManagerService。讀者可參看AMS getCommonServicesLocked函數 Map<String,IBinder> services, BundlecoreSettings)//核心配置參數,目前僅有“long_press”值 ~~~ 對bindApplication的原型分析就到此為止,再來看attachApplicationLocked最后一階段的工作。 (3) attachApplicationLocked分析之三 **ActivityManagerService.java::attachApplicationLocked** ~~~ booleanbadApp = false; booleandidSomething = false; /* 至此,應用進程已經準備好了Android運行環境,下面這句調用代碼將返回ActivityStack中 第一個需要運行的ActivityRecord。由于多線程的原因,難道能保證得到的hr就是我們的目標 Activity嗎? */ ActivityRecord hr = mMainStack.topRunningActivityLocked(null); if (hr !=null && normalMode) { //需要根據processName和uid等確定該Activity是否運行與目標進程有關 if(hr.app == null && app.info.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { //調用AS的realStartActivityLocked啟動該Activity,最后兩個參數為true if (mMainStack.realStartActivityLocked(hr, app, true, true)) { didSomething = true; } } catch (Exception e) { badApp = true; //設置badApp為true } } else{ //如果hr和目標進程無關,則調用ensureActivitiesVisibleLocked函數處理它 mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0); } }// if (hr!= null && normalMode)判斷結束 //mPendingServices存儲那些因目標進程還未啟動而處于等待狀態的ServiceRecord if(!badApp && mPendingServices.size() > 0) { ServiceRecord sr = null; try{ for (int i=0; i<mPendingServices.size(); i++) { sr = mPendingServices.get(i); //和Activity不一樣的是,如果Service不屬于目標進程,則暫不處理 if (app.info.uid != sr.appInfo.uid ||!processName.equals(sr.processName)) continue;//繼續循環 //該Service將運行在目標進程中,所以從mPendingService中移除它 mPendingServices.remove(i); i--; //處理此service的啟動,以后再作分析 realStartServiceLocked(sr, app); didSomething = true;//設置該值為true } } }...... ......//啟動等待的BroadcastReceiver ......//啟動等待的BackupAgent,相關代碼類似Service的啟動 if(badApp) { //如果以上幾個組件啟動有錯誤,則設置badApp為true。此處將調用handleAppDiedLocked //進行處理。該函數我們以后再作分析 handleAppDiedLocked(app, false, true); returnfalse; } /* 調整進程的oom_adj值。didSomething表示在以上流程中是否啟動了Acivity或其他組件。 如果啟動了任一組件,則didSomething為true。讀者以后會知道,這里的啟動只是向 應用進程發出對應的指令,客戶端進程是否成功處理還是未知數。基于這種考慮,所以此處不宜 馬上調節進程的oom_adj。 讀者可簡單地把oom_adj看做一種優先級。如果一個應用進程沒有運行任何組件,那么當內存 出現不足時,該進程是最先被系統殺死的。反之,如果一個進程運行的組件越多,那么它就越不易被 系統殺死以回收內存。updateOomAdjLocked就是根據該進程中組件的情況對應調節進程的 oom_adj值的。 */ if(!didSomething) updateOomAdjLocked(); returntrue; } ~~~ attachApplicationLocked第三階段的工作就是通知應用進程啟動Activity和Service等組件,其中用于啟動Activity的函數是ActivityStack realStartActivityLocked。 此處先來分析應用進程的bindApplication,該函數將為應用進程綁定一個Application。 * * * * * **提示**:還記得AMS中System Context執行的兩次init嗎?第二次init的功能就是將Context和對應的Application綁定在一起。 * * * * * (4) ApplicationThread的bindApplication分析 bindApplication在ApplicationThread中的實現,其代碼如下: **ActivityThread.java::bindApplication** ~~~ public final void bindApplication(......) { if(services != null)//保存AMS傳遞過來的系統Service信息 ServiceManager.initServiceCache(services); //向主線程消息隊列添加SET_CORE_SETTINGS消息 setCoreSettings(coreSettings); //創建一個AppBindData對象,其實就是用來存儲一些參數 AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; data.providers = providers; data.instrumentationName = instrumentationName; ......//將AMS傳過來的參數保存到AppBindData中 //向主線程發送H.BIND_APPLICATION消息 queueOrSendMessage(H.BIND_APPLICATION, data); } ~~~ 由以上代碼可知,ApplicationThread接收到來自AMS的指令后,均會將指令中的參數封裝到一個數據結構中,然后通過發送消息的方式轉交給主線程去處理。BIND_APPLICATION最終將由handleBindApplication函數處理。該函數并不復雜,但是其中有些點是值得關注的,這些點主要是初始化應用進程的一些參數。handleBindApplication函數的代碼如下: **ActivityThread.java::handleBindApplication** ~~~ private void handleBindApplication(AppBindDatadata) { mBoundApplication = data; mConfiguration = new Configuration(data.config); mCompatConfiguration = new Configuration(data.config); //初始化性能統計對象 mProfiler = new Profiler(); mProfiler.profileFile = data.initProfileFile; mProfiler.profileFd = data.initProfileFd; mProfiler.autoStopProfiler = data.initAutoStopProfiler; //設置進程名。從此,之前那個默默無名的進程終于有了自己的名字 Process.setArgV0(data.processName); android.ddm.DdmHandleAppName.setAppName(data.processName); if(data.persistent) { //對于persistent的進程,在低內存設備上,不允許其使用硬件加速顯示 Display display = WindowManagerImpl.getDefault().getDefaultDisplay(); //當內存大于512MB,或者屏幕尺寸大于1024*600,可以使用硬件加速 if(!ActivityManager.isHighEndGfx(display)) HardwareRenderer.disable(false); } //啟動性能統計 if(mProfiler.profileFd != null) mProfiler.startProfiling(); //如果目標SDK版本小于12,則設置AsyncTask使用pool executor,否則使用 //serializedexecutor。這些executor涉及Java Concurrent類,對此不熟悉的讀者 //請自行學習和研究。 if(data.appInfo.targetSdkVersion <= 12) AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR); //設置timezone TimeZone.setDefault(null); //設置語言 Locale.setDefault(data.config.locale); //設置資源及兼容模式 applyConfigurationToResourcesLocked(data.config, data.compatInfo); applyCompatConfiguration(); //根據傳遞過來的ApplicationInfo創建一個對應的LoadApk對象 data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); //對于系統APK,如果當前系統為userdebug/eng版,則需要記錄log信息到dropbox的日志記錄 if((data.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) { StrictMode.conditionallyEnableDebugLogging(); } /* 如目標SDK版本大于9,則不允許在主線程使用網絡操作(如Socketconnect等),否則拋出 NetworkOnMainThreadException,這么做的目的是防止應用程序在主線程中因網絡操作執行 時間過長而造成用戶體驗下降。說實話,沒有必要進行這種限制,在主線程中是否網絡操作 是應用的事情。再說,Socket也可作為進程間通信的手段,在這種情況下,網絡操作耗時很短。 作為系統,不應該設置這種限制。另外,Goolge可以提供一些開發指南或規范來指導開發者, 而不應如此蠻橫地強加限制。 */ if (data.appInfo.targetSdkVersion> 9) StrictMode.enableDeathOnNetwork(); //如果沒有設置屏幕密度,則為Bitmap設置默認的屏幕密度 if((data.appInfo.flags &ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) == 0) Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); if(data.debugMode != IApplicationThread.DEBUG_OFF){ ......//調試模式相關處理 } IBinder b= ServiceManager.getService(Context.CONNECTIVITY_SERVICE); IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); try { //設置Http代理信息 ProxyPropertiesproxyProperties = service.getProxy(); Proxy.setHttpProxySystemProperty(proxyProperties); } catch(RemoteException e) {} if(data.instrumentationName != null){ //在正常情況下,此條件不滿足 } else { //創建Instrumentation對象,在正常情況都再這個條件下執行 mInstrumentation = new Instrumentation(); } //如果Package中聲明了FLAG_LARGE_HEAP,則可跳過虛擬機的內存限制,放心使用內存 if((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) dalvik.system.VMRuntime.getRuntime().clearGrowthLimit(); //創建一個Application,data.info為LoadedApk類型,在其內部會通過Java反射機制 //創建一個在該APK AndroidManifest.xml中聲明的Application對象 Applicationapp = data.info.makeApplication( data.restrictedBackupMode, null); //mInitialApplication保存該進程中第一個創建的Application mInitialApplication = app; //安裝本Package中攜帶的ContentProvider if(!data.restrictedBackupMode){ List<ProviderInfo> providers = data.providers; if(providers != null) { //installContentProviders我們已經分析過了 installContentProviders(app, providers); mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } //調用Application的onCreate函數,做一些初始工作 mInstrumentation.callApplicationOnCreate(app); } ~~~ 由以上代碼可知,bindApplication函數將設置一些初始化參數,其中最重要的有: - 創建一個Application對象,該對象是本進程中運行的第一個Application。 - 如果該Application有ContentProvider,則應安裝它們。 * * * * * **提示**:從以上代碼可知,ContentProvider的創建就在bindApplication函數中,其時機早于其他組件的創建。 * * * * * (5) 應用進程的創建及初始化總結 本節從應用進程的入口函數main開始,分析了應用進程和AMS之間的兩次重要交互,它們分別是: - 在應用進程啟動后,需要盡快調用AMS的attachApplication函數,該函數是這個剛呱呱墜地的應用進程第一次和AMS交互。此時的它還默默“無名”,連一個確定的進程名都沒有。不過沒關系,attachApplication函數將根據創建該應用進程之前所保存的ProcessRecord為其準備一切“手續”。 - attachApplication準備好一切后,將調用應用進程的bindApplication函數,在該函數內部將發消息給主線程,最終該消息由handleBindApplication處理。handleBindApplication將為該進程設置進程名,初始化一些策略和參數信息等。另外,它還創建一個Application對象。同時,如果該Application聲明了ContentProvider,還需要為該進程安裝ContentProvider。 * * * * * **提示**:這個流程有點類似生孩子,一般生之前需要到醫院去登記,生完后又需去注冊戶口,如此這般,這個孩子才會在社會有合法的身份。 * * * * * 6. ActivityStack realStartActivityLocked分析 如前所述,AMS調用完bindApplication后,將通過realStartActivityLocked啟動Activity。在此之前,要創建完應用進程并初始化Android運行環境(除此之外,連ContentProvider都安裝好了)。 **ActivityStack.java::realStartActivityLocked** ~~~ //注意,在本例中該函數的最后兩個參數的值都為true final booleanrealStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { r.startFreezingScreenLocked(app, 0); mService.mWindowManager.setAppVisibility(r,true); if(checkConfig) { ......//處理Config發生變化的情況 mService.updateConfigurationLocked(config, r, false); } r.app =app; app.waitingToKill = null; //將ActivityRecord加到ProcessRecord的activities中保存 int idx= app.activities.indexOf(r); if (idx< 0) app.activities.add(r); //更新進程的調度優先級等,以后再分析該函數 mService.updateLruProcessLocked(app, true, true); try { List<ResultInfo> results = null; List<Intent> newIntents = null; if(andResume) { results = r.results; newIntents = r.newIntents; } if(r.isHomeActivity) mService.mHomeProcess = app; //看看是否有dex對應Package的需要 mService.ensurePackageDexOpt( r.intent.getComponent().getPackageName()); r.sleeping = false; r.forceNewConfig = false; ...... //①通知應用進程啟動Activity app.thread. scheduleLaunchActivity (new Intent(r.intent), r, System.identityHashCode(r), r.info, mService.mConfiguration, r.compat, r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { ......//處理heavy-weight的情況 } } } ......//try結束 r.launchFailed = false; ...... if(andResume) { r.state = ActivityState.RESUMED; r.stopped = false; mResumedActivity = r;//設置mResumedActivity為目標Activity r.task.touchActiveTime(); //添加該任務到近期任務列表中 if(mMainStack) mService.addRecentTaskLocked(r.task); //②關鍵函數,見下文分析 completeResumeLocked(r); //如果在這些過程中,用戶按了Power鍵,怎么辦? checkReadyForSleepLocked(); r.icicle = null; r.haveState = false; }...... //啟動系統設置向導Activity,當系統更新或初次使用時需要進行配置 if(mMainStack) mService.startSetupActivityLocked(); returntrue; } ~~~ 在以上代碼中有兩個關鍵函數,分別是:scheduleLaunchActivity和completeResumeLocked。其中,scheduleLaunchActivity用于和應用進程交互,通知它啟動目標Activity。而completeResumeLocked將繼續AMS的處理流程。先來看第一個關鍵函數。 (1) scheduleLaunchActivity函數分析 **ActivityThread.java::scheduleLaunchActivity** ~~~ public final void scheduleLaunchActivity(Intentintent, IBinder token, int ident, ActivityInfo info, Configuration curConfig,CompatibilityInfo compatInfo, Bundlestate, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, booleanisForward, StringprofileName, ParcelFileDescriptor profileFd, booleanautoStopProfiler) { ActivityClientRecord r = new ActivityClientRecord(); ......//保存AMS發送過來的參數信息 //向主線程發送消息,該消息的處理在handleLaunchActivity中進行 queueOrSendMessage(H.LAUNCH_ACTIVITY, r); } ~~~ **ActivityThread.java::handleMessage** ~~~ public void handleMessage(Message msg) { switch(msg.what) { case LAUNCH_ACTIVITY: { ActivityClientRecord r = (ActivityClientRecord)msg.obj; //根據ApplicationInfo得到對應的PackageInfo r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); //調用handleLaunchActivity處理 handleLaunchActivity(r, null); }break; ...... } ~~~ **ActivityThread.java::handleLaunchActivity** ~~~ private voidhandleLaunchActivity(ActivityClientRecord r, Intent customIntent){ unscheduleGcIdler(); if (r.profileFd != null) {......//略去} handleConfigurationChanged(null, null); /* ①創建Activity,通過Java反射機制創建目標Activity,將在內部完成Activity生命周期 的前兩步,即調用其onCreate和onStart函數。至此,我們的目標com.dfp.test.TestActivity 創建完畢 */ Activitya = performLaunchActivity(r, customIntent); if (a !=null) { r.createdConfig = new Configuration(mConfiguration); BundleoldState = r.state; //②調用handleResumeActivity,其內部有個關鍵點,見下文分析 handleResumeActivity(r.token, false, r.isForward); if(!r.activity.mFinished && r.startsNotResumed) { .......// . r.paused = true; }else { //如果啟動錯誤,通知AMS ActivityManagerNative.getDefault() .finishActivity(r.token,Activity.RESULT_CANCELED, null); } } ~~~ handleLaunchActivity的工作包括: - 首先調用performLaunchActivity,該在函數內部通過Java反射機制創建目標Activity,然后調用它的onCreate及onStart函數。 - 調用handleResumeActivity,會在其內部調用目標Activity的onResume函數。除此之外,handleResumeActivity還完成了一件很重要的事情,見下面的代碼: **ActivityThread.java::handleResumeActivity** ~~~ final void handleResumeActivity(IBinder token,boolean clearHide, booleanisForward) { unscheduleGcIdler(); //內部調用目標Activity的onResume函數 ActivityClientRecord r = performResumeActivity(token, clearHide); if (r !=null) { finalActivity a = r.activity; final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; ...... if(!r.onlyLocalRequest) { //將上面完成onResume的Activity保存到mNewActivities中 r.nextIdle = mNewActivities; mNewActivities = r; //①向消息隊列中添加一個Idler對象 Looper.myQueue().addIdleHandler(new Idler()); } r.onlyLocalRequest = false; ...... } ~~~ 根據第2章對MessageQueue的分析,當消息隊列中沒有其他要處理的消息時,將處理以上代碼中通過addIdleHandler添加的Idler對象,也就是說,Idler對象的優先級最低,這是不是說它的工作不重要呢?非也。至少在handleResumeActivity函數中添加的這個Idler并不不簡單,其代碼如下: **ActivityThread.java::Idler** ~~~ private class Idler implements MessageQueue.IdleHandler{ publicfinal boolean queueIdle() { ActivityClientRecord a = mNewActivities; booleanstopProfiling = false; ...... if (a !=null) { mNewActivities = null; IActivityManager am = ActivityManagerNative.getDefault(); ActivityClientRecord prev; do { if(a.activity != null && !a.activity.mFinished) { //調用AMS的activityIdle am.activityIdle(a.token, a.createdConfig, stopProfiling); a.createdConfig = null; } prev =a; a =a.nextIdle; prev.nextIdle = null; } while(a != null); //do循環結束 }//if(a!=null)判斷結束 ...... ensureJitEnabled(); returnfalse; }// queueIdle函數結束 } ~~~ 由以上代碼可知,Idler將為那些已經完成onResume的Activity調用AMS的activityIdle函數。該函數是Activity成功創建并啟動的流程中與AMS交互的最后一步。雖然對應用進程來說,Idler處理的優先級最低,但AMS似乎不這么認為,因為它還設置了超時等待,以處理應用進程沒有及時調用activityIdle的情況。這個超時等待即由realStartActivityLocked中最后一個關鍵點completeResumeLocked函數設置。 (2) completeResumeLocked函數分析 **ActivityStack.java::completeResumeLocked** ~~~ private final voidcompleteResumeLocked(ActivityRecord next) { next.idle = false; next.results = null; next.newIntents = null; //發送一個超時處理消息,默認為10秒。IDLE_TIMEOUT_MSG就是針對acitivityIdle函數的 Messagemsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); msg.obj= next; mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT); //通知AMS if(mMainStack) mService.reportResumedActivityLocked(next); ......//略去其他邏輯的代碼 } ~~~ 由以上代碼可知,AMS給了應用進程10秒的時間,希望它在10秒內調用activityIdle函數。這個時間不算長,和前面AMS等待應用進程啟動的超時時間一樣。所以,筆者有些困惑,為什么要把這么重要的操作放到idler中去做。 下面來看activityIdle函數,在其內部將調用ActivityStack activityIdleInternal。 (3) activityIdleInternal函數分析 **ActivityStack.java::activityIdleInternal** ~~~ final ActivityRecord activityIdleInternal(IBindertoken, boolean fromTimeout, Configuration config) { /* 如果應用進程在超時時間內調用了activityIdleInternal函數,則fromTimeout為false 否則,一旦超時,在IDLE_TIMEOUT_MSG的消息處理中也會調用該函數,并設置fromTimeout 為true */ ActivityRecord res = null; ArrayList<ActivityRecord> stops = null; ArrayList<ActivityRecord> finishes = null; ArrayList<ActivityRecord> thumbnails = null; int NS =0; int NF =0; int NT =0; IApplicationThread sendThumbnail = null; booleanbooting = false; booleanenableScreen = false; synchronized (mService) { //從消息隊列中撤銷IDLE_TIMEOUT_MSG if(token != null) mHandler.removeMessages(IDLE_TIMEOUT_MSG, token); int index= indexOfTokenLocked(token); if(index >= 0) { ActivityRecord r = mHistory.get(index); res =r; //注意,只有fromTimeout為true,才會走執行下面的條件語句 if(fromTimeout) reportActivityLaunchedLocked(fromTimeout, r, -1, -1); if(config != null) r.configuration =config; /* mLaunchingActivity是一個WakeLock,它能防止在操作Activity過程中掉電,同時 這個WakeLock又不能長時間使用,否則有可能耗費過多電量。所以,系統設置了一個超時 處理消息LAUNCH_TIMEOUT_MSG,超時時間為10秒。一旦目標Activity啟動成功, 就需要需要釋放 WakeLock */ if(mResumedActivity == r && mLaunchingActivity.isHeld()) { mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); mLaunchingActivity.release(); } r.idle = true; mService.scheduleAppGcsLocked(); ...... ensureActivitiesVisibleLocked(null, 0); if(mMainStack) { if(!mService.mBooted) { mService.mBooted = true; enableScreen = true; } }//if (mMainStack)判斷結束 } else if(fromTimeout) {//注意,只有fromTimeout為true,才會走下面的case reportActivityLaunchedLocked(fromTimeout, null, -1, -1); } /* ①processStoppingActivitiesLocked函數返回那些因本次Activity啟動而 被暫停(paused)的Activity */ stops =processStoppingActivitiesLocked(true); ...... for (i=0;i<NS; i++) { ActivityRecord r = (ActivityRecord)stops.get(i); synchronized (mService) { //如果這些Acitivity 處于finishing狀態,則通知它們執行Destroy操作,最終它們 //的onDestroy函數會被調用 if(r.finishing) finishCurrentActivityLocked(r, FINISH_IMMEDIATELY); else //否則將通知它們執行stop操作,最終Activity的onStop被調用 stopActivityLocked(r); }//synchronized結束 }//for循環結束 ......//處理等待結束的Activities //發送ACTION_BOOT_COMPLETED廣播 if(booting) mService.finishBooting(); ...... returnres; } ~~~ 在activityIdleInternal中有一個非常重要的關鍵點,即處理那些因為本次Activity啟動而被暫停的Activity。有兩種情況需考慮: - 如果被暫停的Activity處于finishing狀態(例如Activity在其onStop中調用了finish函數),則調用finishCurrentActivityLocked。 - 否則,要調用stopActivityLocked處理暫停的Activity。 此處涉及除AMS和目標進程外的第三個進程,即被切換到后臺的那個進程。不過至此,我們的目標Activity終于正式登上了歷史舞臺。 * * * * * **提示**:本例的分析結束了嗎?沒有。因為am設置了-W選項,所以其實我們還在startActivityAndWait函數中等待結果。ActivityStack中有兩個函數能夠觸發AMS notifyAll,一個是reportActivityLaunchedLocked,另一個是reportActivityVisibleLocked。前面介紹的activityInternal函數只在fromTimeout為true時才會調用reportActivityLaunchedLocked,而本例中fromTimeout為false,如何是好?該問題的解答非常復雜,姑且先一語帶過:當Activity顯示出來時,其在AMS中對應ActivityRecord對象的windowVisible函數將被調用,其內部會觸發reportActivityLaunchedLocked函數,這樣我們的startActivityAndWait才能被喚醒。 * * * * * 7. startActivity分析之后半程總結 總結startActivity后半部分的流程,主要涉及目標進程和AMS的交互,如圖6-15所示。 :-: ![](http://img.blog.csdn.net/20150803123243623?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖6-15 startActivity后半程總結 圖6-15中涉及16個重要函數調用,而且這僅是startActivity后半部分的調用流程,可見整個流程有多么復雜! 8. startPausingLocked函數分析 現在我們分析圖6-14中的startPausingLocked分支。根據前面的介紹,當啟動一個新Activity時,系統將先行處理當前的Activity,即調用startPausingLocked函數來暫停當前Activity。 (1) startPausingLocked分析 **ActivityStack.java::startPausingLocked** ~~~ private final void startPausingLocked(booleanuserLeaving, boolean uiSleeping) { //mResumedActivity保存當前正顯示的Activity, ActivityRecord prev = mResumedActivity; mResumedActivity = null; //設置mPausingActivity為當前Activity mPausingActivity = prev; mLastPausedActivity = prev; prev.state = ActivityState.PAUSING;//設置狀態為PAUSING prev.task.touchActiveTime(); ...... if(prev.app != null && prev.app.thread != null) { try { //①調用當前Activity所在進程的schedulePauseActivity函數 prev.app.thread.schedulePauseActivity(prev,prev.finishing, userLeaving,prev.configChangeFlags); if(mMainStack) mService.updateUsageStats(prev, false); } ......//catch分支 }......//else分支 if(!mService.mSleeping && !mService.mShuttingDown) { //獲取WakeLock,以防止在Activity切換過程中掉電 mLaunchingActivity.acquire(); if(!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) { Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT); } } if(mPausingActivity != null) { //暫停輸入事件派發 if(!uiSleeping) prev.pauseKeyDispatchingLocked(); //設置PAUSE超時,時間為500毫秒,這個時間相對較短 Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG); msg.obj = prev; mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT); }......//else分支 } ~~~ startPausingLocked將調用應用進程的schedulePauseActivity函數,并設置500毫秒的超時時間,所以應用進程需盡快完成相關處理。和scheduleLaunchActivity一樣,schedulePauseActivity將向ActivityThread主線程發送PAUSE_ACTIVITY消息,最終該消息由handlePauseActivity來處理。 (2) handlePauseActivity分析 **ActivityThread.java::handlePauseActivity** ~~~ private void handlePauseActivity(IBinder token,boolean finished, boolean userLeaving, int configChanges){ //當Activity處于finishing狀態時,finished參數為true,不過在本例中該值為false ActivityClientRecord r = mActivities.get(token); if (r !=null) { //調用Activity的onUserLeaving函數, if(userLeaving) performUserLeavingActivity(r); r.activity.mConfigChangeFlags |=configChanges; //調用Activity的onPause函數 performPauseActivity(token, finished, r.isPreHoneycomb()); ...... try { //調用AMS的activityPaused函數 ActivityManagerNative.getDefault().activityPaused(token); }...... } } ~~~ **ActivityManagerService.java::activityPaused** ~~~ public final void activityPaused(IBinder token) { ...... mMainStack.activityPaused(token, false); } ~~~ **ActivityStack.java::activityPaused** ~~~ final void activityPaused(IBinder token, booleantimeout) { ActivityRecord r = null; synchronized (mService) { int index= indexOfTokenLocked(token); if (index>= 0) { r =mHistory.get(index); //從消息隊列中撤銷PAUSE_TIMEOUT_MSG消息 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if(mPausingActivity == r) { r.state = ActivityState.PAUSED;//設置ActivityRecord的狀態 completePauseLocked();//完成本次Pause操作 }...... } } ~~~ (3) completePauseLocked分析 **ActivityStack.java::completePauseLocked** ~~~ private final void completePauseLocked() { ActivityRecord prev = mPausingActivity; if (prev!= null) { if(prev.finishing) { prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE); } elseif (prev.app != null) { if(prev.configDestroy) { destroyActivityLocked(prev, true, false); } else { //①將剛才被暫停的Activity保存到mStoppingActivities中 mStoppingActivities.add(prev); if(mStoppingActivities.size() > 3) { //如果被暫停的Activity超過3個,則發送IDLE_NOW_MSG消息,該消息最終 //由我們前面介紹的activeIdleInternal處理 scheduleIdleLocked(); } } //設置mPausingActivity為null,這是圖6-14②、③分支的分割點 mPausingActivity = null; } //②resumeTopActivityLocked將啟動目標Activity if(!mService.isSleeping()) resumeTopActivityLocked(prev); ...... } ~~~ 就本例而言,以上代碼還算簡單,最后還是通過resumeTopActivityLocked來啟動目標Activity。當然,由于之前已經設置了mPausingActivity為null,所以最終會走到圖6-14中③的分支。 (4) stopActivityLocked分析 根據前面的介紹,此次目標Activity將走完onCreate、onStart和onResume流程,但是被暫停的Activity才剛走完onPause流程,那么它的onStop什么時候調用呢? 答案就在activityIdelInternal中,它將為mStoppingActivities中的成員調用stopActivityLocked函數。 **ActivityStack.java::stopActivityLocked** ~~~ privatefinal void stopActivityLocked(ActivityRecord r) { if((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) { if(!r.finishing) { requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, "no-history"); } } elseif (r.app != null && r.app.thread != null) { try { r.stopped = false; //設置STOPPING狀態,并調用對應的scheduleStopActivity函數 r.state = ActivityState.STOPPING; r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags); }...... } ~~~ 對應進程的scheduleStopActivity函數將根據visible的情況,向主線程消息循環發送H. STOP_ACTIVITY_HIDE或H. STOP_ACTIVITY_SHOW消息。不論哪種情況,最終都由handleStopActivity來處理。 **ActivityThread.java::handleStopActivity** ~~~ private void handleStopActivity(IBinder token,boolean show, int configChanges) { ActivityClientRecord r = mActivities.get(token); r.activity.mConfigChangeFlags |= configChanges; StopInfoinfo = new StopInfo(); //調用Activity的onStop函數 performStopActivityInner(r, info, show, true); ...... try { //調用AMS的activityStopped函數 ActivityManagerNative.getDefault().activityStopped( r.token, r.state, info.thumbnail, info.description); } } ~~~ AMS沒有為stop設置超時消息處理。嚴格來說,還是有超時限制的,只是這個超時處理與activityIdleInternal結合起來了。 (5) startPausingLocked總結 總結startPausingLocked流程,如圖6-16所示。 :-: ![](http://img.blog.csdn.net/20150803123302838?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖6-16 startPausingActivity流程總結 圖6-16比較簡單,讀者最好結合代碼再把流程走一遍,以加深理解。 9. startActivity總結 Activity的啟動就介紹到這里。這一路分析下來,相信讀者也和筆者一樣覺得此行絕不輕松。先回顧一下此次旅程: - 行程的起點是am。am是Android中很重要的程序,讀者務必要掌握它的用法。我們利用am start命令,發起本次目標Activity的啟動請求。 - 接下來進入ActivityManagerService和ActivityStack這兩個核心類。對于啟動Activity來說,這段行程又可分細分為兩個階段:第一階段的主要工作就是根據啟動模式和啟動標志找到或創建ActivityRecord及對應的TaskRecord;第二階段工作就是處理Activity啟動或切換相關的工作。 - 首先討論了AMS直接創建目標進程并運行Activity的流程,其中涉及目標進程的創建,在目標進程中Android運行環境的初始化,目標Activity的創建以及觸發onCreate、onStart及onResume等其生命周期中重要函數調用等相關知識點。 - 接著又討論了AMS先pause當前Activity,然后再創建目標進程并運行Activity的流程。其中牽扯到兩個應用進程和AMS的交互,其難度之大可見一斑。 讀者在閱讀本節時,務必要區分此旅程中兩個階段工作的重點:其一是找到合適的ActivityRecord和TaskRecord;其二是調度相關進程進行Activity切換。在SDK文檔中,介紹最為詳細的是第一階段中系統的處理策略,例如啟動模式、啟動標志的作用等。第二階段工作其實是與Android組件調度相關的工作。SDK文檔只是針對單個Activity進行生命周期方面的介紹。 坦誠地說,這次旅程略過不少邏輯情況。原因有二,一方面受限于精力和篇幅,另方面是作為調度核心類,和AMS相關的代碼及處理邏輯非常復雜,而且其間還夾雜了與WMS的交互邏輯,使復雜度更甚。再者,筆者個人感覺這部分代碼絕談不上高效、嚴謹和美觀,甚至有些丑陋(在分析它們的過程中,遠沒有研究Audio、Surface時那種暢快淋漓的感覺)。 此處列出幾個供讀者深入研究的點: - 各種啟動模式、啟動標志的處理流程。 - Configuration發生變化時Activity的處理,以及在Activity中對狀態保存及恢復的處理流程。 - Activity生命周期各個階段的轉換及相關處理。Android 2.3以后新增的與Fragment的生命周期相關的轉換及處理。 >[info]**建議**:在研究代碼前,先仔細閱讀SDK文檔相關內容,以獲取必要的感性認識,否則直接看代碼很容易迷失方向。 [^①]:關于Zygote的工作原理,請讀者閱讀卷I第4章“深入理解Zygote”
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看