天天看點

Android之四種加載方式

在多activity開發中,有可能是自己應用之間的activity跳轉,或者夾帶其他應用的可複用activity。可能會希望跳轉到原來某個activity執行個體,而不是産生大量重複的activity。

這需要為activity配置特定的加載模式,而不是使用預設的加載模式。

activity有四種加載模式:

standard

singletop

singletask

singleinstance

設定的位置在androidmanifest.xml檔案中activity元素的android:launchmode屬性:

<activity android:name="actb" android:launchmode="singletask"></activity>

也可以在eclipse adt中圖形界面中編輯:

Android之四種加載方式

區分activity的加載模式,通過示例一目了然。這裡編寫了一個activity a(acta)和activity b(actb)循環跳轉的例子。對加載模式修改和代碼做稍微改動,就可以說明四種模式的差別。

首先說standard模式,也就是預設模式,不需要配置launchmode。先隻寫一個名為acta的activity:

package com.easymorse.activities; import android.app.activity; import android.content.intent; import android.os.bundle; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.linearlayout; import android.widget.textview; public class acta extends activity {     /** called when the activity is first created. */     @override     public void oncreate(bundle savedinstancestate) {         super.oncreate(savedinstancestate);         textview textview = new textview(this);         textview.settext(this + "");         button button = new button(this);         button.settext("go acta");         button.setonclicklistener(new onclicklistener() {             @override             public void onclick(view v) {                 intent intent = new intent();                 intent.setclass(acta.this, acta.class);                 startactivity(intent);             }         });         linearlayout layout = new linearlayout(this);         layout.setorientation(linearlayout.vertical);         layout.addview(textview);         layout.addview(button);         this.setcontentview(layout);     } }

例子中都沒有用layout,免得看着羅嗦。可見是acta –> acta的例子。在界面中列印出對象的tostring值可以根據hash code識别是否建立新acta執行個體。

第一個界面:

Android之四種加載方式

點選按鈕後:

Android之四種加載方式

可以多點幾次。發現每次都建立了該activity的新執行個體。standard的加載模式就是這樣的,intent将發送給新的執行個體。

現在點android裝置的回退鍵,可以看到是按照剛才建立activity執行個體的倒序依次出現,類似退棧的操作,而剛才操作跳轉按鈕的過程是壓棧的操作。如下圖:

Android之四種加載方式

singletop和standard模式,都會将intent發送新的執行個體(後兩種模式不發送到新的執行個體,如果已經有了的話)。不過,singletop要求如果建立intent的時候棧頂已經有要建立的activity的執行個體,則将intent發送給該執行個體,而不發送給新的執行個體。

還是用剛才的示例,隻需将launchmode改為singletop,就能看到差別。

運作的時候會發現,按多少遍按鈕,都是相同的actia執行個體,因為該執行個體在棧頂,是以不會建立新的執行個體。如果回退,将退出應用。

Android之四種加載方式

singletop模式,可用來解決棧頂多個重複相同的activity的問題。

如果是a activity跳轉到b activity,再跳轉到a activity,行為就和standard一樣了,會在b activity跳轉到a activity的時候建立a activity的新執行個體,因為當時的棧頂不是a activity執行個體。

acta類稍作改動:

        button.settext("go actb");                 intent.setclass(acta.this, actb.class);

actb類:

public class actb extends activity {     protected void oncreate(bundle savedinstancestate) {          button button=new button(this);             button.settext("go acta");             button.setonclicklistener(new onclicklistener() {                 @override                 public void onclick(view v) {                     intent intent=new intent();                     intent.setclass(actb.this, acta.class);                     startactivity(intent);                 }             });             linearlayout layout=new linearlayout(this);             layout.addview(button);             this.setcontentview(layout);

actb類使用預設(standard)加載,acta使用singletop加載。結果類似下圖:

Android之四種加載方式

如果把acta的加載模式改為standard,情況一樣。

singletask模式和後面的singleinstance模式都是隻建立一個執行個體的。

當intent到來,需要建立singletask模式activity的時候,系統會檢查棧裡面是否已經有該activity的執行個體。如果有直接将intent發送給它。

把上面singletop的執行個體中的acta的launchmode改為singletask,actb的改為standard。那麼會發現在acta界面中按一次按鈕:

Android之四種加載方式

然後在actb1界面中按按鈕,因為acta是singletask,會使用原來的acta1執行個體。這時候棧内的情況:

Android之四種加載方式

如果多次按按鈕跳轉,會發現始終隻有acta1這一個acta類的執行個體。

解釋singleinstance模式比較麻煩。

首先要說一下task(任務)的概念。

如果是swing或者windows程式,可能有多個視窗可以切換,但是你無法在自己程式中複用人家的視窗。注意是直接複用人家的二進制代碼,不是你拿到人家api後的源代碼級調用。

android可以做到,讓别人的程式直接複用你的activity(類似桌面程式的視窗)。

android為提供這種機制,就引入了task的概念。task可以認為是一個棧,可放入多個activity。比如啟動一個應用,那麼 android就建立了一個task,然後啟動這個應用的入口activity,就是intent-filter中配置為main和launch的那個(見一個apk檔案部署産生多個應用安裝的效果)。這個activity是根(root)activity,可能會在它的界面調用其他activity,這些activity如果按照上面那三個模式,也會在這個棧(task)中,隻是執行個體化的政策不同而已。

驗證的辦法是調用和列印activity的taskid:

textview textview2 = new textview(this); textview2.settext("task id: "+this.gettaskid());

會發現,無論切換activity,taskid是相同的。

當然也可以在這個單一的task棧中,放入别人的activity,比如google地圖,這樣使用者看過地圖按回退鍵的時候,會退棧回到調用地圖的activity。對使用者來說,并不覺得在操作多個應用。這就是task的作用。

但是,有這樣的需求,多個task共享一個activity(singletask是在一個task中共享一個activity)。

現成的例子是google地圖。比如我有一個應用是導遊方面的,其中調用的google地圖activity。那麼現在我比如按home鍵,然後到應用清單中打開google地圖,你會發現顯示的就是剛才的地圖,實際上是同一個activity。

如果使用上面三種模式,是無法實作這個需求的。google地圖應用中有多個上下文activity,比如路線查詢等的,導遊應用也有一些上下文activity。在各自應用中回退要回退到各自的上下文activity中。

singleinstance模式解決了這個問題(繞了這麼半天才說到正題)。讓這個模式下的activity單獨在一個task棧中。這個棧隻有一個activity。導遊應用和google地圖應用發送的intent都由這個activity接收和展示。

這裡又有兩個問題:

如果是這種情況,多個task棧也可以看作一個應用。比如導遊應用啟動地圖activity,實際上是在導遊應用task棧之上 singleinstance模式建立的(如果還沒有的話,如果有就是直接顯示它)一個新棧,當這個棧裡面的唯一activity,地圖activity 回退的時候,隻是把這個棧移開了,這樣就看到導遊應用剛才的activity了;

多個應用(task)共享一個activity要求這些應用都沒有退出,比如剛才強調要用home鍵從導遊應用切換到地圖應用。因為,如果退出導遊應用,而這時也地圖應用并未運作的話,那個單獨的地圖activity(task)也會退出了。

如果還是拿剛才的acta和actb的示例,可以把actb的模式改為singleinstance,acta為standard,如果按一次按鈕切換到actb,看到現象用示意圖類似這樣:

Android之四種加載方式

如果是第一次按鈕切換到actb,在actb在按按鈕切換到acta,然後再回退,示意圖是:

Android之四種加載方式

另外,可以看到兩個activity的taskid是不同的。

**********************************這是分割線*****************************

注意:(一)當選擇後兩種方式時 如果按home鍵退出,再長按home鍵進入,此時onnewintent不被通路,因為再次進入的時候沒有被發起intent 。隻有調用startactivity(intent i);時才會激活onnewintent。(這種特性可以用來監聽到home鍵,哈哈)

(二)如果選擇加載模式為singleinstance,則這時如果有用到tts的話,在進行tts檢測時在onactivityresult中傳回的結果會是texttospeech.engine.check_voice_data_fail;而其他模式則不會出現這種情況,具體原因還有待研究。

繼續閱讀