深入剖析Android四大組件(八)——結束Activity的4個階段

      網友投稿 918 2022-05-28

      上一篇博文介紹了啟動Activity請求的流程以及對相關數據結構的處理,那么當我們試圖結束Activity的時候,ActivityManagerService的行為將會是怎樣的呢?這一節將介紹結束Activity的3種主要方法和4個階段。

      1.結束Activity的3種主要方法

      結束Activity時,我們通常采用如下3種主要方法。

      ①以編程的方式結束Activity

      該方法即在代碼中顯式調用Activity的finish()方法。一般來說,我們經常會遇到這樣的需求——點擊某個按鈕退出界面,此時只需要在按鈕的點擊事件中添加finish()方法即可。finish()方法的代碼如下所示,finish()內部默認調用finish(false):

      private void finish(boolean finishTask) { if (mParent == null) { int resultCode; Intent resultData; synchronized (this) { resultCode = mResultCode; resultData = mResultData; } if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { if (resultData != null) { resultData.prepareToLeaveProcess(); } if (ActivityManagerNative.getDefault() .finishActivity(mToken, resultCode, resultData, finishTask)) { mFinished = true; } } catch (RemoteException e) { // Empty } } else { mParent.finishFromChild(this); } }

      以上紅色標注的為主要方法。

      ②按鍵盤(硬鍵盤或者軟鍵盤)上Back鍵來結束Activity

      這種情況下,不需要添加任何代碼就可以結束Activity,但需要注意的是,并不是所有的設備都會有Back鍵。在未加定制的Android代碼中,它為每個Activity界面提供了軟鍵盤。

      當單擊此按鈕的時候,系統將會通過回調onBackPressed()方法告知Activity Back按鍵已經按下。onBackPressed()方法的代碼如下所示:

      public void onBackPressed() { if (mActionBar != null && mActionBar.collapseActionView()) { return; } if (!mFragments.getFragmentManager().popBackStackImmediate()) { finishAfterTransition(); } }

      finishAfterTransition()代碼如下:

      public void finishAfterTransition() { if (!mActivityTransitionState.startExitBackTransition(this)) { finish(); } }

      通過上面的代碼可知,onBackPressed()方法的本質還是一個finish()方法。當然,也可以屏蔽這種行為,只需要在我們自行實現的Activity的onBackPressed()方法中取消調用super.onBackPressed()即可,但是我們不建議這樣做。

      ③使用Home鍵使當前顯示的Activity消失,回到Launcher首頁

      與Back鍵一樣,并不是所有的設備都會提供硬Home按鍵。在未加定制的Android代碼中,它為每個Activity界面提供了軟鍵盤。

      通常,應用程序無法捕獲Home鍵,除非強行捕獲,但不建議這么做。這個鍵將會由PhoneWindowManager處理,具體代碼如下:

      void startDockOrHome(){ Intent dock=createHomeDockIntent(); if(dock!=null){ try{ mContext.startActivity(dock); return; }catch(ActivityNotFoundException e){ } } mContext.startActivity(mHomeIntent); }

      最終,Android會以mHomeIntent去啟動Launcher從而使得當前Activity退居后臺,Launcher被重新顯示出來。mHomeIntent是這樣定義的:

      mHomeIntent=new Intent(Intent.ACTION_MAIN,null);

      mHomeIntent.addCateGory(Intent.CATEGORY_HOME);

      mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

      2.結束Activity的4個階段

      同啟動Activity一樣,結束Activity也有4個階段,下面我們將對其進行詳細講解。

      ①第一階段——參數初始化以及參數傳遞

      與啟動Activity相同,結束Activity同樣需要ActivityManagerProxy將命令轉發出去的。當按下Back鍵時,將會執行下面的這行代碼:

      ActivityManagerNative.getDefault().finishActivity(mToken,resultCode,resultData);

      這時,ActivityManagerProxy會調用它的finishActivity()方法將參數寫入Parcel中并轉發出去。finishActivity()方法代碼如下:

      public boolean finishActivity(IBinder token,int resultCode,Intent resultData)throws RemoteException{ Parcel data=Parcel.obtain(); Parcel reply=Parcel.obtain(); ...... //轉發指令 mRemote.transact(FINISH_ACTIVITY_TRANSACTION,data,reply,0); ...... return res; }

      調用finishActivity()時,Android會回調ActivityManagerService的onTransact()方法,轉而執行其基類(也就是ActivityManagerNative類)的onTransact()方法來向ActivityManagerService()發送請求:

      @Override public boolean onTransact(int code,Parcel data,Parcel reply,int flags)throws RemoteException{ ...... case FINISH_ACTIVITY_TRANSACTION:{ data.enforceInterface(IActivityManager.descriptor); IBinder token=data.readStrongBinder(); Intent resultData=null; int resultCode=data.readInt(); if(data.readInt()!=0){ resultData=Intent.CREATOR.createFromParcel(data); } boolean res=finishActivity(token,resultCode,resultData); reply.writeNoException();; reply.writeInt(res ? 1 : 0); return true; } ...... }

      至此,參數處理以及指令發送的前驅工作已經完成,接下來的工作將由ActivityManagerService完成。

      ②第二階段——獲取需要結束的Activity的記錄信息

      在第二階段,首先要做的是用ActivityManagerService調用ActivityStack的requestFinishActivityLocked()方法執行信息手機工作,具體代碼如下所示:

      public final boolean finishActivity(IBinder token,int resultCode,Intent resultData){ ...... final long origId= Binder.clearCallingIdentity(); boolean res=mMainStack.requestFinishActivityLocked(token,resultCode,resultData,"app-request"); Binder.restoreCallingIdentity(origId); return res; }

      而在requestFinishActivityLocked()方法中,首先將使用indexOfTokenLocked()方法獲取該Activity在啟動Activity的歷史記錄(mHistory)中的偏移量,然后從啟動歷史記錄中獲取該Activity的記錄信息(ActivityRecord),具體代碼如下所示:

      final boolean requestFinishActivityLocked(IBinder token,int resultCode,Intent resultData,String reason){ ....... //獲取索引 int index=indexOfTokenLocked(token); ...... //從歷史記錄中獲取Activity記錄信息 ActivityRecord r=mHistory.get(index); //啟動結束流程的下一個階段 finishActivityLocked(r,index,resultCode,resultData,reason); return true; }

      其中indexOfTokenLocked()方法的關鍵代碼如下所示:

      final int indexOfTokenLocked(IBinder token){ ActivityRecord r=(ActivityRecord)token; return mHistory.indexOf(r);//獲取需要結束的Activity在mHistory的所引值 ...... }

      ③第三階段——處理需要結束的Activity信息

      在第三階段中,我們已經獲取到需要結束的Activity的記錄信息,這里需要對它們進行一些處理,這通過finishActivityLocked()方法完成。finishActivityLocked()方法的流程下圖所示:

      該圖有一下幾點需要特別說明一下。

      Ⅰ代碼中r.makeFinishing()的作用是將Activity的正在結束標志置為true,并且將該Activity所在的Activity棧的Activity數量減一,這就為了后續操作做好了準備。makeFinishing()方法的代碼如下所示:

      void makeFinishing(){ if(!finishing){ finishing=true;//標識正在結束 if(task!=null && inHistory){ task.numActivities--;//同一個棧的Activity數量減一 } } }

      Ⅱ由于當前的Activity即將結束,它至少會被另一個Activity覆蓋,這時當前Activity窗口則不應該繼續將按鍵消息分發到當前Activity上。為完成這個需求,ActivityManagerService會調用pauseKeyDispatchingLocked()方法,該方法的代碼如下所示:

      void pauseKeyDispatchingLocked(){ if(!keyPaused){ keysPaused=true; service.mWindowManager.pauseKeyDispatching(this); } }

      Ⅲ假設使用startActivityForResult的方式啟動當前Activity,那么結束此Activity時需要給調用的Activity傳送處理的結果。這里使用如下代碼完成:

      resultTo.addResultLocked(r,r.resultWho,r.requestCode,resultCode,resultData);

      下面我們來看看addResultLocked()方法的行為,具體如下代碼所示:

      void addResultLocked(ActivityRecord from,String resultWho,int requestCode,int resultCode,Intent resultData){ Instrumentation.ActivityResult r=new Instrumentation.ActivityResult(from,resultWho,requestCode,resultCode,resultData); if(results==null){ results=new ArrayList(); } results.add(r); }

      ④第四階段——Activity間調度準備

      在第三階段中,我們完成了對一些Activity棧以及窗口的處理,為Activity調度做了一些準備工作,然后啟動了ActivityThread的調度流程:

      startPausingLocked(false,false);

      其中startPausingLocked()方法的關鍵代碼如下所示:

      深入剖析Android四大組件(八)——結束Activity的4個階段

      private final void startPausingLocked(boolean userLeaving,boolean uiSleeping){ ..... mResumedActivity=null; mPausingActivity=prev; mLastPausedActivity=prev; prev.state=ActivityState.PAUSING; prev.task.touchActiveTime(); prev.updateThumbnail(screenshotActivities(prev),null); ..... prev.app.thread.schedulePauseActivity(prev,prev.finishing,userleaving,pre.configChangeFlags); }

      通過學習啟動Activity的4個階段,我們知道ActivityStack啟動了ActivityThread的Activity間的調度,這里就要講解當Activity將要被結束時Activity之間的調度行為。schedulePauseActivity()方法用于完成這個任務,其代碼如下所示:

      public final void schedulePauseActivity(IBinder token, boolean finished,boolean userLeaving,int configChanges){ queueOrSendMessage( finished? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,token, (userLeaving?1:0), configChanges); }

      此處,queueOrSendMessage()方法拼裝了一個消息,發往用于處理消息的handler。在Handler中,最終調用handlePauseActivity()方法處理這個消息。handlePauseActivity()方法的代碼如下所示:

      private void handlePauseActivity(IBinder token,boolean finished, boolean userLeaving,int configChanges){ ActivityClientRecord r=mActivities.get(token); ...... //暫停當前的Activity performPauseActivity(token,finished,r.isPreHoneycomb()); //通知服務操作完成 ActivityManagerNative.getDefault().activityPaused(token); }

      該方法主要完成以下兩件事情。

      Ⅰ調用performPauseActivity()方法回調Activity的onPause等回調接口,并設置Activity的狀態,具體流程如下圖所示:

      Ⅱ調用代理的activityPaused()方法,轉發Activity的一個pause指令到Activity管理服務,代碼如下所示:

      public boolean onTransact(int code,Parcel data,Parcel reply,int flags)throws RemoteException{ case ACTIVITY_PAUSED_TRANSACTION:{ ...... IBinder token=data.readStrongBinder(); activityPaused(token); ...... return true; } }

      Activity管理服務的activityPaused()方法主要完成該Activity其他生命周期的調度以及恢復前一個Activity,其中的部分關鍵代碼如下所示:

      private final void completePauseLocked(){ ActivityRecord prev=mPausingActivity; ...... prev=finishCurrentActivityLocked(prev,FINISH_AFTER_VISIBLE); ..... destroyActivityLocked(prev,true,false); ..... resumeTopActivityLocked(prev);//恢復一個Activity if(prev !=null){ //允許窗口分發按鍵消息到此Activity(prev) prev.resumeKeyDispatchingLocked(); } ..... prev.cpuTimeAtResume=0;//重置 }

      當前一個Activity被重新顯示出來的時候,它需要具有捕獲按鍵消息的能力,因此這里調用了resumeKeyDispatchingLocked()方法來完成這個需求。resumeKeyDispatchingLocked()方法的作用是恢復對這個Activity的按鍵分發,具體代碼如下所示:

      void resumeKeyDispatchingLocked(){ if(keysPaused){ keysPaused=false; service.mWindowManager.resumeKeyDispatching(false); } }

      至此,原來顯示的Activity由于按下了Back鍵而消失,而覆蓋在它下面的那個Activity則被重新顯示出來了。

      注意1:按Home鍵與按下Back鍵或以Activity的finish()方法結束Activity不同是的,按Home鍵是強制顯示Launcher而使得其他Activity被Launcher覆蓋,而按下Back鍵或以Activity的finish()方法結束Activity,則是因為當前的Activity消失而導致在它下面的Activity被顯示出來。它們有著本質的區別,請注意這個注意。

      注意2:只要應用程序啟動,進程將會被保留,除非應用程序發生了嚴重的異常或者使用別的工具(比如DDMS)殺掉進程,就算我們按下Back鍵結束Activity了,其應用程序進程依然一直存在于設備中。

      ??

      Android

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:LiteOS內核源碼分析系列一 盤點那些重要的數據結構 (1)
      下一篇:鼠標單擊就選中一個字的解決辦法
      相關文章
      久久精品国产亚洲av成人| 亚洲高清国产拍精品26U| 亚洲一区二区三区电影| 亚洲男人的天堂www| 亚洲午夜福利精品久久| 亚洲av高清在线观看一区二区| 亚洲aⅴ天堂av天堂无码麻豆| 亚洲av中文无码字幕色不卡| 亚洲精品乱码久久久久久蜜桃图片| 亚洲人成色77777在线观看| 亚洲国产精品无码久久98| 亚洲爆乳大丰满无码专区| 亚洲av成人片在线观看| 亚洲第一网站男人都懂| 久99精品视频在线观看婷亚洲片国产一区一级在线 | 久久亚洲免费视频| 久久亚洲精品国产精品黑人| 久久青青草原亚洲AV无码麻豆| 亚洲av无码专区在线播放| 亚洲av福利无码无一区二区 | 亚洲综合精品成人| 亚洲成a人片在线观看天堂无码| 亚洲国产aⅴ成人精品无吗| 欧洲亚洲综合一区二区三区| 国产av无码专区亚洲av毛片搜| 亚洲国产成人精品女人久久久| 亚洲视频一区二区| 亚洲人成人无码网www电影首页| 亚洲国产精品嫩草影院在线观看 | 亚洲av第一网站久章草| 精品国产香蕉伊思人在线在线亚洲一区二区 | 亚洲成年人免费网站| 亚洲国产精品张柏芝在线观看| 亚洲一区二区三区免费观看| 亚洲中文字幕精品久久| 亚洲成AⅤ人影院在线观看| 亚洲综合AV在线在线播放| 久久夜色精品国产亚洲AV动态图| 亚洲精品资源在线| 亚洲中文字幕乱码AV波多JI| 亚洲AV无码一区二区三区在线观看|