Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 徹底明白Activity啟動模式-SingleTop、SingleTask、SingleInstance具體使用場景

徹底明白Activity啟動模式-SingleTop、SingleTask、SingleInstance具體使用場景

編輯:關於Android編程

啟動模式

啟動模式是什麼

有這樣的場景:

當我們使用App的時候,呈現出一個Activity,按下返回鍵(不考慮重寫返回鍵事件),常常就回退到上一個打開的Activity或者退出App。
//重寫返回按鍵事件
public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            // 這裡重寫返回鍵
            return true;
        }
        return false;
    }
幾個應用之間,例如我的App要使用拍照功能,我需要調用系統的相機App,這分明就是兩個不同的應用程序,分別運行在不同的進程,但是當我調用完成相機後,按下返回鍵可以返回我的App
//調用相機
private void openCamera() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        uri = PhotoUtil.createImageFile();
        if (takePictureIntent.resolveActivity(mContext.getPackageManager()) != null) {// 相機被卸載時不會崩潰
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAMERA);
        }
    }
還有例如在我們的App打開浏覽器、微博之類的應用,然後跳轉到浏覽器,使用完成浏覽器的功能,不斷按下返回鍵,可以回到我們的應用。而不是像點擊桌面上浏覽器圖標啟動浏覽器那樣,不斷返回鍵,最後回到的是桌面。 生成重復頁面。
以前遇到過這樣的Bug:
消息推送,通知欄彈出Notification,點擊Notification跳轉到指定Activity,但是如果我現在頁面就停留在那個指定的Activity,會再次打開我當前的Activity,這樣返回的時候回退的頁面和當前頁面一樣,感官上就會很奇怪。 登錄的時候,登錄成功跳轉到主頁,按下兩次登錄按鈕,生成了兩個主頁。一些有啟動延遲的頁面(往往是動畫,網絡造成)也會有這樣的情況。

為什麼要研究啟動模式

有時候我們的App需要生成給其他App調用的Activity,例如浏覽器應用,照相機應用 解決生成重復頁面等等Bug 任務棧過深的時候,避免一直按返回鍵也退不回想要的頁面

任務棧

任務棧Task,是一種用來放置Activity實例的容器,他是以棧的形式進行盛放,也就是所謂的先進後出,主要有2個基本操作:壓棧和出棧,其所存放的Activity是不支持重新排序的,只能根據壓棧和出棧操作更改Activity的順序。

啟動一個Application的時候,系統會為它默認創建一個對應的Task,用來放置根Activity。默認啟動Activity會放在同一個Task中,新啟動的Activity會被壓入啟動它的那個Activity的棧中,並且顯示它。當用戶按下回退鍵時,這個Activity就會被彈出棧,按下Home鍵回到桌面,再啟動另一個應用,這時候之前那個Task就被移到後台,成為後台任務棧,而剛啟動的那個Task就被調到前台,成為前台任務棧,手機頁面顯示的就是前台任務棧中的棧頂元素。

standard

Activity默認模式,所有的Activity遵循元素進棧出棧的特性,例如進棧序列為A->B->C->D,D呈現在頁面上,按返回鍵出棧順序久違D->C->B->A.

singleTop

棧頂復用模式,如果要開啟的activity在任務棧的頂部已經存在,就不會創建新的實例,而是調用 onNewIntent() 方法。避免棧頂的activity被重復的創建。
在開始處,我們提到的2個Bug,可以用這種模式解決

消息推送

通知欄彈出Notification,點擊Notification跳轉到指定Activity,但是如果我現在頁面就停留在那個指定的Activity,會再次打開我當前的Activity,這樣返回的時候回退的頁面和當前頁面一樣,感官上就會很奇怪。

這裡寫圖片描述

        btnNotification.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                NoticationUtil.sendNotication(mContext);
            }
        });
public class NoticationUtil {

    @SuppressLint("NewApi")
    public static void sendNotication(Context context) {
        // 在Android進行通知處理,首先需要重系統哪裡獲得通知管理器NotificationManager,它是一個系統Service。
        NotificationManager manager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
                new Intent(context, MainActivity.class), 0);
        // 通過Notification.Builder來創建通知,注意API Level
        // API16之後才支持
        Notification notify = new Notification.Builder(context)
                .setSmallIcon(R.drawable.ic_launcher)
                .setTicker("您有新短消息,請注意查收!").setContentTitle("我是標題")
                .setContentText("點我打開主頁").setContentIntent(pendingIntent)
                .setNumber(1).build();

        notify.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明當通知被用戶點擊時,通知將被清除。
        manager.notify(1, notify);
    }
}
        
            
                

                
            
        

修改為:

        
            
                

                
            
        

這裡寫圖片描述

登錄的時候

登錄成功跳轉到主頁,按下兩次登錄按鈕,生成了兩個主頁。一些有啟動延遲的頁面(往往是動畫,網絡造成)也會有這樣的情況。

        btnLogin.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // 延遲1000秒,模擬網絡超時
                v.getHandler().postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        // 登錄成功,跳轉到主頁
                        LoginSuccessActivity.actionStart(mContext);
                    }
                }, 3000);

            }
        });

XML清單配置

       

這裡寫圖片描述
把他修改為singleTop

        

這裡寫圖片描述

耗時操作返回頁面

還有一種場景 從activity A啟動了個service進行耗時操作,或者某種監聽,這個時候你home鍵了,service收集到信息,要返回activityA了,就用singleTop啟動,實際不會創建新的activityA,只是resume了。不過使用standard又會創造2個A的實例。

singleTask

棧內復用模式

如果要啟動的Activity在當前棧內啟動,activity只會在任務棧裡面存在一個實例。如果要激活的activity,在任務棧裡面已經存在,就不會創建新的activity,而是復用這個已經存在的activity,調用 onNewIntent() 方法,並且清空這個activity任務棧上面所有的activity。
        
            
                
                
            
        
        
        

XML配置 MainActivitiy,Test2Activity為standard模式,Test1Activity為singleTask模式。
入棧情況:
MainActivity—>Test1Activity—>Test2Activity—>MainActivity,如果此時啟動Test1Activity,會清空Test1頂部元素 棧內情況變為MainActivity—>Test1Activity;
這裡寫圖片描述
2. 主要就是在清單文件中配置android:taskAffinity="新的包名",因為android:taskAffinity這個字段默認指定的包名為本應用的包名,表示在本應用包名的任務棧內創建應用。如果設置了這個字段,而且還和本應用包名不同,就會在新的任務棧創建任務。例如,修改剛才的代碼Test1Activity的配置清單:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">

指定android:taskAffinity包名為com.test.newlaunch
這裡寫圖片描述

06-03 14:47:46.556: D/displayTask(22127): id:--69
06-03 14:47:46.556: D/displayTask(22127): numActivities:--1
06-03 14:47:46.556: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.MainActivity
06-03 14:47:46.556: D/displayTask(22127): baseActivity:--com.example.testlaunch.aty.MainActivity
06-03 14:47:48.428: D/displayTask(22127): id:--69
06-03 14:47:48.428: D/displayTask(22127): numActivities:--2
06-03 14:47:48.428: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.Test2Activity
06-03 14:47:48.428: D/displayTask(22127): baseActivity:--com.example.testlaunch.aty.MainActivity
06-03 14:47:50.080: D/displayTask(22127): id:--69
06-03 14:47:50.080: D/displayTask(22127): numActivities:--3
06-03 14:47:50.080: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.MainActivity
06-03 14:47:50.080: D/displayTask(22127): baseActivity:--com.example.testlaunch.aty.MainActivity
06-03 14:47:55.164: D/displayTask(22127): id:--71
06-03 14:47:55.164: D/displayTask(22127): numActivities:--1
06-03 14:47:55.164: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.Test1Activity
06-03 14:47:55.164: D/displayTask(22127): baseActivity:--com.example.testlaunch.aty.Test1Activity
06-03 14:47:56.688: D/displayTask(22127): id:--71
06-03 14:47:56.688: D/displayTask(22127): numActivities:--2
06-03 14:47:56.688: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.Test2Activity
06-03 14:47:56.688: D/displayTask(22127): baseActivity:--com.example.testlaunch.aty.Test1Activity
06-03 14:47:58.140: D/displayTask(22127): id:--71
06-03 14:47:58.140: D/displayTask(22127): numActivities:--3
06-03 14:47:58.140: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.MainActivity
06-03 14:47:58.140: D/displayTask(22127): baseActivity:--com.example.testlaunch.aty.Test1Activity

最開始的棧id為69,然後在06-03 14:47:55.164: D/displayTask(22127): topActivity:--com.example.testlaunch.aty.Test1ActivityTest1Activity啟動的時候又新建了一個棧id為71.同時在查看後台程序,看到出現了2個後台任務都是我們的應用—TestLanuch。
這裡寫圖片描述

作用

做浏覽器、微博之類的應用,比如其他App需要打開我們的浏覽器頁面,就可以配置他為singleTask模式,保證他只有一個唯一實例,節約內存同時按下返回鍵後的感官也更順暢。但是需要注意,提供給人調用的頁面最好是棧底元素。因為,如果自己的客戶端處於運行狀態,按下Home鍵後台掛起。此時如果使用如果其他應用(比如說QQ)調起自己的客戶端某個頁面,不做任何處理的情況下,按下回退或者當前 Activity.finish(),頁面都會停留在自己的客戶端(因為自己的Application回 退棧不為空),這明顯不符合邏輯的。
對於我的應用TestLaunch XML清單配置
        
            
                


                
            
        
        
            
                

                
            
        
        

在其他應用 “我是QQ”中打開指定頁面Test1Activity

        btnSkip.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(".aty.Test1Activity");
                try {
                    startActivity(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

錯誤重現

a. 模擬操作TestLanuch
這裡寫圖片描述
b. 使用我是QQ打開TestLanuch的Test1Activity,然後按返回鍵退棧
這裡寫圖片描述
要解決這個問題有兩種方式:

利用singleTask清除這個activity任務棧上面所有的activity特性

我們提供的分享頁面始終是我們棧底的元素,只要他一啟動就會清空任務棧內其他Activity,保證只有他一個實例。

android:taskAffinity=”要打開本應用的其他應用包名”

例如在本應用真就設置:

        
            
                

                
            
        

這樣在Test1Activity啟動的時候,如果com.test.newlaunch沒啟動就新建一個任務棧,如果com.test.newlaunch啟動了就直接加入它的任務棧。

SingleInstance

單一實例模式,整個手機操作系統裡面只有一個實例存在。不同的應用去打開這個activity 共享公用的同一個activity。他會運行在自己單獨,獨立的任務棧裡面,並且任務棧裡面只有他一個實例存在。應用場景:呼叫來電界面。這種模式的使用情況比較罕見,在Launcher中可能使用。或者你確定你需要使Activity只有一個實例。
可以得出以下結論:
1. 以singleInstance模式啟動的Activity具有全局唯一性,即整個系統中只會存在一個這樣的實例。
2. 以singleInstance模式啟動的Activity在整個系統中是單例的,如果在啟動這樣的Activiyt時,已經存在了一個實例,那麼會把它所在的任務調度到前台,重用這個實例。
3. 以singleInstance模式啟動的Activity具有獨占性,即它會獨自占用一個任務,被他開啟的任何activity都會運行在其他任務中。
4. 被singleInstance模式的Activity開啟的其他activity,能夠在新的任務中啟動,但不一定開啟新的任務,也可能在已有的一個任務中開啟。

換句話說,其實SingleInstance就是我們剛才分析的SingleTask中,分享Activity為棧底元素的情況。

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