Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Activity啟動模式與任務棧(Task)全面深入記錄(下)

Activity啟動模式與任務棧(Task)全面深入記錄(下)

編輯:關於Android編程

??通過上一篇文件的分析,我們對Activity的啟動模式有了比較清晰的了解後,本篇我們將繼續對Activity啟動模式的相關參數和任務棧分析,接下來我們就繼續上一篇的問題,如何通過taskAffinity屬性在同一個應用中創建多個任務棧進行探究。

任務棧之taskAffinity屬性

TaskAffinity特點如下:
* TaskAffinity 參數標識著Activity所需要的任務棧的名稱,默認情況下,一個應用中所有Activity所需要的任務棧名稱都為該應用的包名。
* TaskAffinity 屬性一般跟singleTask模式或者跟allowTaskReparenting屬性結合使用,在其他情況下沒有實際意義。
* TaskAffinity屬性的值不能與當前應用包名相同,否則其值跟作廢沒兩樣。

TaskAffinity和singleTask啟動模式結合使用

??當TaskAffinity和singleTask啟動模式結合使用時,當前Activity的任務棧名稱將與TaskAffinity屬性指定的值相同,下面我們通過代碼來驗證,我們同過MainActivity來啟動ActivityA,其中MainActivity啟動模式為默認模式,ActivityA啟動模式為singleTask,而TaskAffinity屬性值為android:taskAffinity="com.zejian.singleTask.affinity" MainActivity和ActivityA代碼如下:

package comzejian.myapplication;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn= (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(MainActivity.this,ActivityA.class);
                startActivity(i);
            }
        });
    }
}

ActivityA.class 代碼如下:

package comzejian.myapplication;

import android.app.Activity;
import android.os.Bundle;

/**
 * Created by zejian
 * Time 16/7/26.
 * Description:
 */
public class ActivityA extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
    }
}

清單文件代碼如下:




    
        
            
                

                
            
        

        

    

??現在我們啟動MainActivity,後再啟動ActivityA,然後我們通過 adb shell dumpsys activity activities 命令查看此時棧的情況:
\
??我們可以清楚地看到兩個任務棧,其中一個是id=249,棧名稱為com.zejian.singleTask.affinity的任務棧,該棧包含了ActivityA,另外一個則是id=248,棧名為默認包名的任務棧,包含了MainActivity。到此我們也明白了,我們確實可以通過singleTask與android:taskAffinity屬性相結合的方式來指定我們Activity所需要的棧名稱,使相應的Activity存在於不同的棧中,圖解如下:
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoNCBpZD0="當taskaffinity和allowtaskreparenting結合使用">當TaskAffinity和allowTaskReparenting結合使用

??首先我們來聊聊allowTaskReparenting屬性,它的主要作用是activity的遷移,即從一個task遷移到另一個task,這個遷移跟activity的taskAffinity有關。當allowTaskReparenting的值為“true”時,則表示Activity能從啟動的Task移動到有著affinity的Task(當這個Task進入到前台時),當allowTaskReparenting的值為“false”,表示它必須呆在啟動時呆在的那個Task裡。如果這個特性沒有被設定,元素(當然也可以作用在每次activity元素上)上的allowTaskReparenting屬性的值會應用到Activity上。默認值為“false”。這樣說可能還比較難理解,我們舉個例子,比如現在有兩個應用A和B,A啟動了B的一個ActivityC,然後按Home鍵回到桌面,再單擊B應用時,如果此時,allowTaskReparenting的值為“true”,那麼這個時候並不會啟動B的主Activity,而是直接顯示已被應用A啟動的ActivityC,我們也可以認為ActivityC從A的任務棧轉移到了B的任務棧中。這就好比我們在路邊收養了一只與主人走失了的貓,養著養著突然有一天,主人找上門來了,這只貓也就被帶回去了。我們通過圖解來更好地理解這種情景:
\
??我們通過代碼層面來驗證一下,我們創建兩個應用分別為ActivityTask(簡稱A應用)和ActivityTask2(簡稱B應用),其中A包含ActivityA,B包含ActivityC,我們通過ActivityA啟動B應用中的ActivityC,再回到桌面,啟動B應用,此時我們觀察A應用和B應用各自棧的變化(因為A,B為不同的應用所以taskAfinity屬性值肯定不同,所以這裡我們就沒必要指定了)。
ActivityA及其清單文件代碼如下:

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/**
 * Created by zejian
 * Time 16/7/23.
 * Description:
 */
public class ActivityA extends Activity {

    private Button btnC;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);

        btnC= (Button) findViewById(R.id.mainC);
        btnC.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_LAUNCHER);
                ComponentName cn = new ComponentName("com.cmcm.activitytask2", "com.cmcm.activitytask2.ActivityC");
                intent.setComponent(cn);
                startActivity(intent);
            }
        });
    }
}

     
           
           
     

ActivityA及其清單文件代碼如下:

package com.cmcm.activitytask;

package com.cmcm.activitytask;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/**
 * Created by zejian
 * Time 16/7/23.
 * Description:
 */
public class ActivityC extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_c);
    }
}

??我們通過AcitivityA啟動B應用的ActivityC後,內存中棧的如下:
\
??我們可以看到ActivityA和ActivityC同在一個棧中,接著我們回到桌面啟動B應用,此時內存中的任務棧如下:

??我們發現ActivityC從A應用的任務棧直接移動到B應用的任務棧,這也就符合我們前面所說的現象了。而當我們修改allowTaskReparenting為false時,再運行,然後重復上面的操作,查看內存中任務棧的變化:
A應用啟動B應用的ActivityC時

??回到桌面再啟動B應用時

??對比發現,如果allowTaskReparenting值為false時,ActivityC並不會直接從A應用的任務棧遷移到B應用的任務棧,而是B應用直接重新創建了ActivityC的實例。到此我們對於allowTaskReparenting和taskAffinity屬性的了解就已經相當深入了,不過有點需要說明的是allowTaskReparenting僅限於singleTop和standard模式,這是因為一個activity的affinity屬性由它的taskAffinity屬性定義(代表棧名),而一個task的affinity由它的root activity定義。所以,一個task的root activity總是擁有和它所在task相同的affinity。由於以singleTask和singleInstance啟動的activity只能是一個task的root activity,因此allowTaskReparenting僅限於以standard 和singleTop啟動的activity,大家可以自行測試一下,這裡我們就不測試了哈,下面我們再來說說它們可能應用用場景。

TaskAffinity與allowTaskReparenting和singleTask結合時可能發生的應用場景

TaskAffinity與singleTask應用場景

??假如現在有這麼一個需求,我們的客戶端app正處於後台運行,此時我們因為某些需要,讓微信調用自己客戶端app的某個頁面,用戶完成相關操作後,我們不做任何處理,按下回退或者當前Activity.finish(),頁面都會停留在自己的客戶端(此時我們的app回退棧不為空),這顯然不符合邏輯的,用戶體驗也是相當出問題的。我們要求是,回退必須回到微信客戶端,而且要保證不殺死自己的app.這時候我們的處理方案就是,設置當前被調起Activity的屬性為:

LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"

其中com.tencent.mm是借助於工具找到的微信包名,就是把自己的Activity放到微信默認的Task棧裡面,這樣回退時就會遵循“Task只要有Activity一定從本Task剩余Activity回退”的原則,不會回到自己的客戶端;而且也不會影響自己客戶端本來的Activity和Task邏輯。

TaskAffinity與allowTaskReparenting應用場景

??一個e-mail應用消息包含一個網頁鏈接,點擊這個鏈接將出發一個activity來顯示這個頁面,雖然這個activity是浏覽器應用定義的,但是activity由於e-mail應用程序加載的,所以在這個時候該activity也屬於e-mail這個task。如果e-mail應用切換到後台,浏覽器在下次打開時由於allowTaskReparenting值為true,此時浏覽器就會顯示該activity而不顯示浏覽器主界面,同時actvity也將從e-mail的任務棧遷移到浏覽器的任務棧,下次打開e-買了時並不會再顯示該activity
??到此TaskAffinity就全部介紹完了,最後我們再來了解幾個跟任務棧相關的屬性參數;

清空任務棧

??Android系統除了給我提供了TaskAffinity來指定任務棧名稱外,還給我提供了清空任務棧的方法,在一般情況下我們只需要在標簽中指明相應的屬性值即可。

android:clearTaskOnLaunch

??這個屬性用來標記是否從task清除除根Activity之外的所有的Activity,“true”表示清除,“false”表示不清除,默認為“false”。這裡有點我們必須要注意的,這個屬性只對任務棧內的root Activity起作用,任務棧內其他的Activity都會被忽略。如果android:clearTaskOnLaunch屬性為“true”,每次我們重新進入這個應用時,我們只會看到根Activity,任務棧中的其他Activity都會被清除出棧。
??比如一個應用的Activity A,B,C,其中clearTaskOnLaunch設置為true,C為默認值,我們依次啟動A,B,C,點擊HOME,再在桌面點擊圖標。啟動的是A,而B,C將都被移除當前任務棧。也就是說,當Activity的屬性clearTaskOnLaunch為true時將被優先啟動,其余的Activity(B、C)都被移除任務棧並銷毀,除非前面A已經finish銷毀,後面的已注冊clearTaskOnLaunch為true的activity(B)才會生效。
??特別地,如果我們的應用中引用到了其他應用的Activity,這些Activity設置了android:allowTaskReparenting屬性為“true”,則它們會被重新宿主到有共同affinity的task中。

android:finishOnTaskLaunch

??finishOnTaskLaunch屬性與clearTaskOnLaunch 有些類似,它們的區別是finishOnTaskLaunch是作用在自己身上(把自己移除任務棧,不影響別的Activity),而clearTaskOnLaunch則是作用在別人身上(把別的Activity移除任務棧),如果我們把Activity的android:finishOnTaskLaunch屬性值設置為true時,離開這個Activity所依賴的任務棧後,當我們重新返回時,該Activity將會被finish掉,而且其他Activity不會受到影響。

android:alwaysRetainTaskState

??alwaysRetainTaskState實際上是給了當前Activity所在的任務棧一個“免死金牌”,如果當前Activity的android:alwaysRetainTaskState設置為true時,那麼該Activity所在的任務棧將不會受到任何清理命令的影響,一直保持當前任務棧的狀態。

好了,到此本篇也就完結,相信通過兩篇的記錄我們對Activity的啟動模式和任務棧都有相對清晰的了解了哈。
 

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