Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 深入剖析Android四大組件(八)——結束Activity的4個階段

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

編輯:關於android開發

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


當我們試圖結束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()方法的關鍵代碼如下所示:

 

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了,其應用程序進程依然一直存在於設備中。

??

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved