天天看點

Activity詳解2

首先,Activity是Android系統中的四大元件之一,可以用于顯示View。Activity是一個與用記互動的系統子產品,幾乎所有的 Activity都是和使用者進行互動的,但是如果這樣就能說Activity主要是用來顯示View就不太正确了。它不僅顯示資料,也傳輸資料,很多程式中使用到MVC模式,

M(Model 模型):Model是應用程式的主體部分,所有的業務邏輯都應該寫在這裡,在Android中Model層與JavaEE中的變化不大,如:對資料庫的操 作,對網絡等的操作都放在該層(但不是說它們都放在同一個包中,可以分開放,但它們統稱為Model層)。

       V(View 視圖):是應用程式中負責生成使用者界面的部分,也是在整個MVC架構中使用者唯一可以看到的一層,接收使用者輸入,顯示處理結果;在Android應用中一般 采用XML檔案裡德界面的描述,使用的時候可以非常友善的引入,當然也可以使用JavaScript+Html等方式作為View。

       C(Controller控制層)android的控制層的重任就要落在衆多的activity的肩上了,是以在這裡就要建議大家不要在activity中寫太多的代碼,盡量能過activity交割Model業務邏輯層處理。

在Android中Activity主要是用來做控制的,它可以選擇要 顯示的View,也可以從View中擷取資料然後把資料傳給Model層進行處理,最後再來顯示出處理結果。

也有一種新的MVP模式:

模型(Model):負責處理資料的加載或者存儲,比如從網絡或本地資料庫擷取資料等;

  視圖(View):負責界面資料的展示,與使用者進行互動;

  主持人(Presenter):相當于協調者,是模型與視圖之間的橋梁,将模型與視圖分離開來。

其中的主要内容這裡不介紹,這裡告訴大家,不僅僅需要學習MVC模式,也需要學習MVP模式,這裡特别不建議大家這樣寫:将所有的事情都交給Activity來完成,比如網絡請求,業務邏輯處理,這樣造成了Activity特别臃腫,一個大的Activity,在後期很難維護。MVC如果用不好,就會将所有的資訊都放在Activity上。

protected void onCreate(Bundle icicle);

       protected void onStart();

      protected void onRestart();

      protected void onResume();

      protected void onPause();

      protected void onStop();

      protected void onDestroy();

 要想了解Activity,那麼就必須要清楚Activity的生命周期,圖檔是最生動的,如下圖:

Activity詳解2

上面一張圖有可能有些模糊,那請看下面這張圖():

Activity詳解2

整個Activity周期,activity建立時調用Oncreate(),這個時候調用setContentView(View)函數,加載xml布局檔案。調用OnStart()方法,Activity顯示在螢幕上,這個時候還不能與使用者互動,調用OnResume()方法,使用者與Activity進行互動,目前Activity被切換的時候,這個時候調用OnPause()方法,在該方法中儲存一些持久資料和釋放占用的資源,這個時候,螢幕還是可見,調用onStop()方法,Activity切換到背景,使用者不可見。onDestroy()activity終止。

Activity的響應時間

       目前Activity所在的線程為主線程,它的響應時間為5秒,如果在目前運作的Activity中進行耗時的操作且響應時間起過5秒,那麼程式就會報ANR錯誤。是以,這也是不建議在Activity中寫太多複雜代碼的原因之一。

       當然,有些代碼隻能寫在Activity中,不然就運作不了(它們不是生命周期方法),比如你想要獲得android系統或者硬體一的些資訊,就必須在Activity中寫出來,如果單獨寫一個工具類獲得不了。

Activity棧

Activity棧儲存了已經啟動并且還沒有終止的所有的Activity,并且我們知道棧是遵從“後進先出”的規則,那麼Activity棧同樣也遵從這樣的規則。

Activity 的狀态與其在Activity棧的位置有着密切的關系。不僅如此,Android系統在資源不足時,也是通過Activity棧來選擇哪些 Activity是可以被終止的,一般來講,Activity系統會優先選擇終止處于目前是停止狀态并且比較靠近Activity棧底的 Activity。

1. Activity的4種狀态

  Activity的生命周期指Activity從啟動到銷毀的過程,Activity有4種狀态:

(1)活動(Active)狀态:這時候Activity處于棧頂,且是可見的,有焦點的,能夠接收使用者輸入前 景Activity。Runtime将試圖不惜一切代價保持它活着,甚至殺死其他Activity以確定它有它所需的資源。當另一個Activity變成Active時,目前 的将變成Paused狀态。

(2)暫停(Paused)狀态:在 某些情況下,你的Activity是可見的,但沒有焦 點,在這時候,Actvity處于Paused狀态。例如,如果有一個透明或非全螢幕上的Activity在你的Actvity上面,你的 Activity将。當處于Paused狀态時,該Actvity仍被認為是Active的,但是它不接受使用者輸入事件。在極端情況下,Runtime将 殺死Paused Activity,以進一步回收資源。當一個Actvity完全被遮住時,它将進入Stopped狀态。

(3)停止(Stopped)狀态:當Activity是不可見的時,Activity處于Stopped狀态。Activity将繼續保留在記憶體中保持目前的所有狀态和成員資訊,假 設系統别的地方需要記憶體的話,這時它是被回收對象的主要候選。當Activity處于Stopped狀态時,一定要儲存目前資料和目前的UI狀态,否則一 旦Activity退出或關閉時,目前的資料和UI狀态就丢失了。

(4)非活動(Inactive)狀态:Activity被殺掉以後或者被啟動以前,處于Inactive狀态。這時Activity已被移除從Activity堆棧中,需要重新啟動才可以顯示和使用。

一些調用執行個體

1)  啟動Activity:onCreate()->onStart()->onResume()->Activityis running 

      2)  按back鍵傳回:onPause()->onStop()->onDestroy() 再次啟動時:onCreate()->onStart()->onResume()->Activityis running 

      3). 按home鍵傳回:onPause()->onStop() 再次啟動時:onRestart()->onStart()->onResume()->Activity isrunning 

      4) 切換到别的Activity(目前Activity不finish),或者采用這個,啟動程式進入Activity,按Home鍵進入桌面,然後長按Home建點選該應用重新進入Activity:onPause()->onStop() 再次啟動時:onRestart()->onStart()->onResume()->Activityis running 

       5) 切換到别的Activity(目前Activity finish):onPause()->onStop()->onDestroy() 再次啟動時:onCreate()->onStart()->onResume()->Activity isrunning 

       6)然後按挂機鍵,進入鎖屏界面,然後從鎖屏界面傳回Activity:onPause()->onResume()

       7)切換到另一個Activity對話框界面,然後按傳回鍵傳回原來的Activity:onPause()->onResume()

       8)啟動本Activity中建立的對話框(彈出Toast和AlertDialog),按傳回鍵從Dialog傳回:Activity的狀态始終未變

       9)無論現在是onPause狀态還是onStop狀态,當系統的記憶體不足時,都會将該Activity殺死,當再次進入該Activity重新建立。這意味着,如果發生這種情況,可能我們的onDestroy(如果在停止狀态被殺死),甚至是onStop都沒有得到執行(如果在暫停狀态被殺死),是以,一些重要的資料和狀态,需要在這種情況下也得到儲存,Google為我們專門提供了一個回調函數,就是onSaveInstanceState(Bundle),通過該函數,可以将這些重要的資料在銷毀前進行儲存,然後再onCreate時重新讀

       10)橫豎屏切換時候activity的生命周期:

                  1、不設定Activity的android:configChanges時,切屏會重新調用各個生命周期,切橫屏時會執行一次,切豎屏時會執行兩次

             2、設定Activity的android:configChanges="orientation"時,切屏還是會重新調用各個生命周期,切橫、豎屏時隻會執行一次

             3、設定Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調用各個生命周期,隻會執行onConfigurationChanged方法

        11)當在MainActivity中按back鍵,退出時,或者在代碼中調用finish(),finishActivity()方法,最終就會走到onDestory()。

  tips:

在多數情況下,你是不需要顯式地調用finish…()方法去銷毀一個activity。在将要讨論到的activity生命周期裡,你可以知道,Android系統會為你管理activity的生命周期,是以你并不需要顯式銷毀activity(即調用finish類方法)。顯式地調用finish類方法,會對使用者的體驗産生不利的影響,除非你确實是不希望使用者傳回到此activity(界面),才去顯式調用finish類方法。

特殊函數

1. startActivityForResult / onActivityResult / setResult 函數組合

提到這類函數組合,相信隻要有過一段時間Android開發的來說都很熟悉了,此函數組合主要用于如下場景:使用者在A Activity上點選某個按鈕,跳轉到B Activity,然後使用者在B Activity上進行一些具體的操作,待操作完成後傳回到A Activity,同時常常将B Activity中操作的一些資料傳回到A Activity中。

再如上場景中,A -> B 需要通過startActivityForResult()方式打開。具體方式如下:

1 button.setOnClickListener(new View.OnClickListener() {
2     @Override
3     public void onClick(View v) {
4         Intent intent = new Intent(AActivity.this, BActivity.class);
5         startActivityForResult(intent, 1);
6     }
7 });      

其中,startActivityForResult第一個參數為Intent,是以,對于需要傳遞額外參數時,可以通過Intent直接傳遞。其中Bundle為可選參數。第二個參數為requestCode,即業務請求碼。

B Activity中,在處理完或相應完使用者操作後,自身結束前,需要通過setResult将資料回傳給A。

1 btnClose.setOnClickListener(new View.OnClickListener() {
 2     public void onClick(View v) {
 3         
 4         // 需要傳回的資料存入到intent中
 5         Intent intent = new Intent();
 6         intent.putExtra("name", "corn");
 7         
 8         //設定傳回資料
 9         setResult(RESULT_OK, intent);
10         
11         //關閉Activity
12         finish();
13     }
14 });      

接下來A接手B回傳的資料。

1 @Override
 2 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
 3     String name; 
 4     // 取得B回傳的資料
 5     if(resultCode == RESULT_OK ){
 6          name = intent.getStringExtra("name");
 7     } else if(resultCode == RESULT_CANCELED){
 8         // ...
 9     }
10 }      

此函數組合中,需要注意如下問題:

1.根據項目的實際需要進行定義,特别需要注意的是,requestCode必須 >= 0,否則此類效果失效,其效果将變成startActivity()效果;

2.resultCode表示B中處理後的結果狀态,系統内部定義了RESULT_OK、RESULT_CANCELED和RESULT_FIRST_USER三種狀态。當然,自己可以定義成任何int型辨別狀态。

3.有時在複雜的業務邏輯中,可能存在A startActivityForResult 到B,同時C也startActivityForResult 到B,且requestCode可能相同(以表示同意業務請求),這時可能需要在B中針對性的判斷此請求來源(來自于A還是C)。此時,可以通過intent傳參形式。相信大家都比較熟悉,其實Activity類中也提供了相應的函數可以擷取到來源Activity的類型函數:getCallingActivity()。但需要注意此函數僅針對startActivityForResult有效,傳回的結果中包含完成包名。

4.A中回調函數調用時機需要注意,其調用發生在B的onPause之後,A的onRestart之前(如果B完成遮住了A),且必然在onResume之前。

5.此函數組合針對B的啟動模式為singleTask或singInstance将會失效。此時,onActivityResult将在A的onpause之後直接回調,且resultCode為RESULT_CANCELED。

2.moveTaskToBack

如《Android總結篇系列:Activity生命周期》一文中有提到的,模拟現在的主流應用最後按Back鍵時不是強制退出應用或直接結束根Activity,而是采取類Home鍵效果,此時可以直接通過此函數實作,非常實用。

1 @Override
2 public void onBackPressed() {
3     moveTaskToBack(true);
4 }      

此方法直接将目前Activity所在的Task移到背景,同時保留activity順序和狀态。

3.onNewIntent調用時機

onNewIntent隻有在以下場景才會回調:目前通過Intent方式啟動的Activity不是重新完整建立執行個體,而是複用之前已經存在的執行個體(如被設定了singleTop啟動模式,或FLAG_ACTIVITY_SINGLE_TOP intent flags或設定了Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP等,以此類推,被設定了singleTask或single。此時都會執行到onNewIntent)。onNewIntent調用後,将繼續回調onRestart,onResume...

4.onSaveInstanceState / onRestoreInstanceState調用時機

onSaveInstanceState調用時機:當Activity變得“容易”被系統銷毀時,onSaveInstanceState即被回調,除非該activity是被使用者主動銷毀的,例如當使用者按BACK鍵的時候。

注意上面的雙引号,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有哪些?

1.當使用者按下HOME鍵時;

2.長按HOME鍵,選擇運作其他的程式時;

3.按下電源按鍵(關閉螢幕顯示)時;

4.從activity A中啟動一個新的activity時;

5.螢幕方向切換時,例如從豎屏切換到橫屏時。

onRestoreInstanceState調用時機,activity A“确實”被系統銷毀了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用。另外,onRestoreInstanceState的bundle參數也會傳遞到onCreate方法中,也可以選擇在onCreate方法中做資料還原。

1 @Override
 2     protected void onRestoreInstanceState(Bundle savedInstanceState) {
 4         super.onRestoreInstanceState(savedInstanceState);
 5         savedInstanceState.getString("name", "");
 7     }
 9     @Override
10     public void onSaveInstanceState(Bundle savedInstanceState) {
11         super.onSaveInstanceState(savedInstanceState);
12         savedInstanceState.putString("name", "corn");
13     }      

或在onCreate中:

1 public class AActivity extends ActionBarActivity {
 2 
 3     private String name;
 4 
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.a);
 9 
10         if (savedInstanceState != null) {
11             name = savedInstanceState.getString("name");
12         }
13 
14     }
15 
16 }      

需要注意的是,onSaveInstanceState被調用時,其調用發生在Activity生命周期中具體的位置。以A->B為例,A中onSaveInstanceState調用發生在A:onPause -> B:onCreate -> B:onResume -> A:onSaveInstanceState -> A:onStop。

onSaveInstanceState常常用于存儲應用程式中目前Activity中重要的狀态資料,以免Activity被系統意外殺掉的情況下當使用者再次回來時不能找到之前的狀态。如同一個Activity中使用多個fragment實作菜單功能時,最好需要在此函數中記錄下目前菜單對應的fragment id等。