【Android筆記】Activity不同狀態間轉換研究

來源:互聯網
上載者:User

      在AndroidSDK發布的官方文檔裡,實際上已經對Activity所包含的各個重要狀態的概念和相互之間轉換機製作了較為詳細的描述,但梨子的滋味總是自己嘗了才能體會的深刻,筆者就在開發一些應用過程中出現過奇怪的錯誤,後來經過確定發現是由於沒有對Activity狀態轉換的一些細節理解到位,從而導致應用本身的一些運行狀態被打亂而出現各種錯誤。因此筆者找個時間特地基於AndroidSDK的Activity描述(AndroidSDK路徑/docs/reference/android/app/Activity.html)做了一些實驗,目的是搞清楚Activity各個重要狀態的轉換機制。

     在做實驗前,先依照官方文檔的介紹理清一些重要狀態的概念,筆者會依照自己開發的經驗和理解加上一些解釋,如果大家覺得有疑問和待商榷的地方也可以提出來探討。

     Android針對Activity的管理使用的是棧,就是說某一個時刻只有一個Activity處在棧頂,當這個Activity被銷毀後,下面的Activity才有可能浮到棧頂,或者有一個新的Activity被建立出來,則舊的Activity就被壓棧沉下去了。從這裡我們可以看出Android似乎是依照一種層次管理所有的Activity的,為什麼這麼做,個人覺得原因在於Android對Activity的作用定位很重要的一點是考慮其管理與使用者互動的作用,而談到互動,首要的就是介面了,因此Activity是直接涉及到與使用者互動的介面處理的,而任意時刻與使用者處於互動狀態的介面只能有一個,所以Android針對Activity的管理採用了具有層次感的棧的資料結構,理解這一點對於Activity一些狀態轉換的細節處理非常重要。

     依據這種基於介面層次性的理解,官方文檔主要提了兩點:

     1. 只有處於棧頂的Activity一定是出於運行狀態的。當啟動一個新的Activity時,系統會將它置於棧頂,同時使其運行,這個時候先前處於棧頂的Activity則被壓棧,只有當棧頂的Activity退出時才可能重新處於棧頂。

     2.一個Activity總的來說擁有四種狀態,分別是

       1)啟用(運行)狀態:  此時它一定是在螢幕的最前端的,對應Activity棧來說,它是在棧頂的;

       2)暫停狀態:此時它在螢幕上仍然是可見的,但是失去了焦點。這種情況會發生在當有一個不會佔滿整個螢幕或者擁有透明屬性的Activity啟動並獲得了螢幕焦點時。一個暫停狀態的Activity還是存活的(alive),它仍然維持著自己的各個內部狀態和成員資訊,仍然和window manager保持串連,但是系統可能會在手機記憶體極低的情況下殺掉該Activity;

       3)終止狀態:此時這個Activity在螢幕上完全不可見,它已經被其他的Activity擋住了。這時它維持著自己的各個內部狀態和成員資訊,但是由於使用者已完全看不見它,其window也會被隱藏掉,也就是說window manager不再管理其window資訊了。停止狀態下的Activity會經常由於手機的記憶體徵用問題被系統殺掉。

       4)當Activity被暫停或者終止時,系統可以把它從記憶體中清除,這個過程也許會提示使用者是否要結束該Activity,也許只會簡單地殺掉其進程。當這個Activity重新被使用者調出來顯示在介面上時,使用者自己必須保證Activity能夠完全恢複先前維護的內部狀態資訊,以使其回到Activity內部所處先前的狀態,以筆者的經驗這種情況如果處理不好很容易出現問題。

      這第二點實際上依據介面層次考慮就是3種情況:

      a.active/running: 此時Activity管理的介面可見,且直接面對和使用者的互動;

      b.pause: 此時Activity管理的介面可見,但處於後端,類似變成了背景;

      c.stop: 此時Activity管理的介面完全不可見。
澄清了上述重要的概念之後,再來看需要研究的Activity具體的函數,很自然可以知道最重要的函數一定就是針對上面三種情況加上Activity自身建立和消毀所涉及到的幾個函數了:

public class Activity extends ApplicationContext {
     protected void onCreate(Bundle savedInstanceState);

     protected void onStart();    
     protected void onRestart();
     protected void onResume();
     protected void onPause();
     protected void onStop();
     protected void onDestroy();
}

筆者對這幾個函數做了測試實驗,以此研究不同狀態間的轉換,和這幾個重要函數的調用順序,實驗非常簡單,兩個Activity之間的跳轉:Activity1的介面上只有一個按鈕,點擊後轉到到Activity2上,Activity2上同樣一個按鈕,點擊後跳到Activity1上。我將上述的函數均打上log,便於觀察,同時為了過濾其他的log,使用了Error層級,純粹是為了方便顯示。

大致代碼如下:

----------------------------------------------------------------------------------------------

public class ALStudyActivity extends Activity {
  private String LOG_TAG = "ALStudyActivity"; 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.e(LOG_TAG,"onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button button01 = (Button)findViewById(R.id.Button01);
        button01.setOnClickListener(new Button.OnClickListener(){
         public void onClick(View v){
          Intent it = new Intent(ALStudyActivity.this,NewActivity.class);
          startActivity(it);
         }
        });
    }
  @Override
  protected void onDestroy() {

      Log.e(LOG_TAG,"onDestroy");
      super.onDestroy();
  }

  .... //其他幾個測試函數,不再列出

}

public class NewActivity extends Activity {
    private String LOG_TAG = "NewActivity";
    @Override
    public void onCreate(Bundle savedInstanceState) {
     Log.e(LOG_TAG,"onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        Button button02 = (Button)findViewById(R.id.Button02);
        button02.setOnClickListener(new Button.OnClickListener(){
         public void onClick(View v){
          Intent it = new Intent(NewActivity.this,ALStudyActivity.class);
          startActivity(it);
         }
        });
    }

    ... //省略,同上

}

----------------------------------------------------------------------------------------------

這裡ALStudyActivity對應Activity1, NewActivity對應Activity2

實驗1. Activity1->按手機(模擬器)Back鍵退出

輸出結果:

 

   

 

 

 

 

 

 

 

 

 

 

 實驗1 顯示了Activity在正常情 況下從正常到退出的全部情形。

   onDestory只有在Activity真正退出時才會被調用。

 

 

 

實驗2. Activity1->手機Home鍵回主介面

輸出結果:

  

 

 

 

 

 

 

 

 

 

 

 

 

 

    實驗2 顯示了一在按手機Home鍵退出時的函數調用時序。由

於不是退出,因此最終沒有調用onDestroy(),但是應用變得不可見,

因此調用了onStop。可以看到系統還調用了儲存執行個體狀態的函數:

onSaveInstanceState(),也就是說按Home鍵退出到主介面系統

會協助我們保持Activity的一些資訊,同時給了我們儲存額外需要維

護的資訊的機會,這一點請注意,因為系統不是在任何時候都會調用onSaveInstanceState()的,下面的實驗將可以看到。

 

實驗3.Activity1->Activity2 

輸出結果:

    

 

 

 

 

 

 

 

 

 

 

 

 從實驗3可以看到系統在Activity1的onPause()之後先將Activity2創

建並顯示出來,然後在執行的Activity1後續的操作,onPause()之後Acti

vity1就不再是最最上層顯示了。

 

 

實驗4.Activity1->Activity2->Activity1

輸出結果:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

實驗4要注意的是,由於我們使用的是

   Intent it = new Intent(...)
   startActivity(it);

的方式,實際上Activity2->Activity1執行的是建立了一個新的

Activity1執行個體,這和一開始的Activity1不是同一個,也就是說,

這裡Activity棧裡已經有了3個Activity執行個體,這一點很容易被弄

錯,可能介面上是好像回到了Activity1,但實際上完全不同。

 

實驗5.Activity1->Activity2->Activity1->Back鍵

輸出結果:

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   實驗5承接了實驗4,只是多按了Back鍵,這時候系統銷毀了

一個Activity1執行個體,棧裡還有兩個,棧頂的是Activity2,也就是

說介面上看見的是Activity2.

  

 

實驗6.Activity1->Activity2->Home鍵->Activity1

輸出結果: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

實驗6的特別地方在於,在按了Home鍵回到主介面後,Activity2也

被轉到後台,程式走到了onStop(),然後我們重新啟動Activity1應

用,可以看到執行onRestart()的是Activity2,而不是Activity1!!

這是因為此時Activity2處在棧頂,這一點也是很容易出錯的地方。

 

 

實驗7.Activity1->旋轉螢幕90度

輸出結果:

 

   

 

 

 

 

 

 

 

 

 

 

 

  實驗7單獨列出來也是因為代表了一類Activity被打斷的情況,就是系統配置發

生了變化,官方文檔裡也對為什麼一定要採用這種先銷毀再重新建立的方式做瞭解

釋,可以參考Configuration Changes一段,我就不再多介紹了,這裡只是實

際跑了個實驗驗證。

 

 

實驗就做這麼多,其實還可以做更多一些情況的測試,但基本的轉換情況這裡都已提到。雖然運行,暫停,退出的基本概念大家比較清楚,但是從上面的實驗也可以看到,一些細節仍然需要在設計時考慮清楚,因為畢竟我們無法估計使用者的操作序列和系統本身的各種情況,如果要想使我們的應用夠健壯,必須充分考慮到Activity不同狀態間轉換的問題。

 

此外,還有一些可以深入探討的問題:

1. 關於finish()的使用,上面的實驗沒有用到它,但我們看到前面的實驗只有在按Back鍵的時候系統會銷毀當前的Activity,如果處理不當會產生大量Activity新的執行個體,而顯然很多不是我們需要的,必須以合適的方式在恰當的時機消除,這時就需要finish(),函數使用雖然簡單,但是要用在什麼時機和地方是需要考慮的。

 

2. 關於onSaveInstanceState()的使用,我們前面的實驗也說明了,不是任何時候系統都會調用onSaveInstanceState()的,原因官方文檔也提了一些,請仔細閱讀,官方舉的一個例子是當一個Activity不再處於最前端時,如果系統由於記憶體原因需要關掉這個Activity,此時系統會調用onSaveInstanceState(),這樣我們才有機會做些儲存資訊以便將來重啟的工作。

 

3.關於onPause()函數,由於Activity從最最上層顯示到其他狀態的轉換會很頻繁,這個函數也會很頻繁的被調到,而它是一個同步函數,這個操作不完成,後面的操作無法進行,因此請不要在其中做太多操作,造成效能問題。

 

4.本文涉及的實驗關於Activity的配置都是使用的standard屬性,沒有涉及"singleTop" /"singleTask" / "singleInstance",這些會影響實驗結果,但是基本的狀態轉換通過標準已經體現出來,是其他的基礎,搞清楚了這些再來研究多個task情況下可能涉及到的Activity跳轉就容易理解了。

 

關於Activity的狀態轉換還有很多可以研究的地方,以後有機會再貼出來。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.