1. 程式人生 > >Android四大元件--Activity詳解

Android四大元件--Activity詳解

本文的主要內容包括1、activity的建立、配置和使用;2、activity的跳轉和傳值;3、startActivityForResult;4、activity的生命週期。

1、activity的建立、配置和使用

Activity是一個應用中的元件,它為使用者提供一個可視的介面,方便使用者操作,比如說拔打電話、照相、發郵件或者是瀏覽地圖等。每個activity會提供一個可視的視窗,一般情況下這個視窗會覆蓋整個螢幕,但在某此情況下也會出現一些比螢幕小的視窗飄浮在另外一個視窗上面。

在 android 中建立一個 Activity 是很簡單的事情,編寫一個繼承自 android.app.Activity的 Java 類並在 AndroidManifest.xml宣告即可。下面是一個為了研究 Activity 生命週期的一個 Activity 例項:

public class Example extends Activity {  
    private static final String LOG_TAG = EX01.class.getSimpleName();  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        Log.e(LOG_TAG, "onCreate");  
    }  
    @Override  
    protected void onStart() {  
        Log.e(LOG_TAG, "onStart");  
        super.onStart();  
    }  
    @Override  
    protected void onResume() {  
        Log.e(LOG_TAG, "onResume");  
        super.onResume();  
    }  
    @Override  
    protected void onPause() {  
        Log.e(LOG_TAG, "onPause");  
        super.onPause();  
    }  
    @Override  
    protected void onStop() {  
        Log.e(LOG_TAG, "onStop");  
        super.onStop();  
    }  
    @Override  
    protected void onDestroy() {  
        Log.e(LOG_TAG, "onDestroy ");  
        super.onDestroy();  
    }  
}  

PS1:

兩個最重要的方法是:
    onCreate()--這個是必須實現的函式,在其中做初始化工作。記住:你必須在此函式中呼叫setContentView()函式的設定Activity的介面。
    onPause()--這個雖然很重要,但不是要必須實現的。此函式在使用者離開Activity時被呼叫(這一般並不表示Activity要被銷燬了)。在這個函式中,你一般需要提交那些需儲存狀態的資料(因為使用者可能不再返回到這個Activity)

Android應用要求所有應用程式元件(Activity,Service,ContentProvider,BroadcastReceiver)都必須顯式進行配置,只要為<application.../>元素新增<activity.../>子元素即可配置Activity。
  如   AndroidManifest.xml   (名單檔案,類似於Web應用中的web.xml檔案)


AndroidManifest.xml 中通過 <activity> 節點說明 Activity,將 apk 檔案安裝後,系統根據這裡的說明來查詢讀取 Activity,本例中的說明如下:

<activity android:name=".Example" android:label="@string/app_name">   
     <intent-filter>   
         <action android:name="android.intent.action.MAIN" />   
         <category android:name="android.intent.category.LAUNCHER" />   
     </intent-filter>   
</activity>  

PS2:
對於<activity.../>這個標籤中的內容如說明如下:
name:指定該Activity的實現類
icon:指定該Activity的對應的圖示
label:指定該Activity的標籤
     除此之外,配置Activity時通常還需要指定一個或多個<intent-filter.../>元素,該元素用於指定該Activity可響應的Intent。
注意看<activity android:name=".Example"/>,看到activity name的值中,最前面有個”.”,如果你把它忘了,程式執行就會出錯,而你很難找出錯誤的原因。

其次,不論你是Activity是隻內部使用還是外部使用,都要去AndroidManifest.xml 名單檔案中註冊,否則依然會出現莫名其妙的錯誤,只是在內部使用時,不需要為acitivity增加意圖過濾器。

其中,
<intent-filter>中就是過濾器。<action>說明此Acitivity是程式的”main”入口,<category>指出這個Acitivity需要在系統的應用列表中列出。
    如果你寫的程式中的Activity不需被其它程式呼叫,那麼不需為這個Activity增加任何intent過慮器過濾器。但程式中必須有一個 Activity被指定為”main” Action和”launcher” category。你自己程中的 Activity可以用更直接的方式呼叫。   

 然而,如果你想讓你的Activity被其它程式呼叫,那麼你需要為它增加意圖過濾器。這些過意圖濾器包括<action>,<category>以及<data>。這些元素指明瞭你的activity響應何種型別的 intent。關於intent過濾器,這裡不再詳細說明了。

2、Activity之間跳轉和傳值

前面我們瞭解瞭如何啟動一個Activity,一個Activity在啟動另外一個Activity的時候可能會遇到需要傳值的需要。Activity之間傳值是通過Bundle來實現的。

1)Activity跳轉介紹

最常見最一般的頁面跳轉程式碼,很簡單,如下:

Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);   
startActivity(intent);   


當然,OtherActivity同樣需要在 AndroidManifest.xml 中定義。

2)Bundle傳值介紹

1、如果資料比較少,比如只要傳一個名字,那麼只要j加一句"intent.putExtra("Name", "feng88724");"即可,程式碼如下:

Intent intent = new Intent();
intent.setClass(A.this, B.class);
intent.putExtra("Name", "feng88724");
startActivity(intent);

2、如果資料比較多,就需要使用 Bundle類了,程式碼如下:

如果我們想要給“收件人”Activity 說點什麼的話,那麼可以通過下面這封“e-mail”來將我們訊息傳遞出去:

Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);  
// 建立一個帶“收件人地址”的 email   
Bundle bundle =new Bundle();// 建立 email 內容  
bundle.putBoolean("boolean_key", true);// 編寫內容  
bundle.putString("string_key", "string_value");   
intent.putExtra("key", bundle);// 封裝 email   
startActivity(intent);// 啟動新的 Activity  

那麼“收件人”該如何收信呢?在 OtherActivity類的 onCreate()或者其它任何地方使用下面的程式碼就可以開啟這封“e-mail”閱讀其中的資訊:

Intent intent =getIntent();// 收取 email   
Bundle bundle =intent.getBundleExtra("key");// 開啟 email   
bundle.getBoolean("boolean_key");// 讀取內容  
bundle.getString("string_key");  


3、startActivityForResult用法詳解

有時,在頁面跳轉之後,需要返回到之前的頁面,同時要保留使用者之前輸入的資訊,這個時候該怎麼辦呢?

在頁面跳轉後,前一個Activity已經被destroy了。如果要返回並顯示資料,就必須將前一個Activity再次喚醒,同時呼叫某個方法來獲取並顯示資料。

要實現這個效果,需要做以下幾步:

1. 首先,從A頁面跳轉到B頁面時,不可以使用"startActivity()"方法,而要使用"startActivityForResult"方法。

2. 在A頁面的Activity中,需要重寫"onActivityResult"方法

onActivityResult(int requestCode, int resultCode, Intent data)

第一個引數:這個整數requestCode提供給onActivityResult,是以便確認返回的資料是從哪個Activity返回的。 這個requestCode和startActivityForResult中的requestCode相對應。
第二個引數:這整數resultCode是由子Activity通過其setResult()方法返回。

第三個引數:一個Intent物件,帶有返回的資料。

 例如:

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

public class MainActivity extends Activity {
    private final static String TAG="MainActivity";
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button btnOpen=(Button)this.findViewById(R.id.btnOpen);
        btnOpen.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                //得到新開啟Activity關閉後返回的資料
                //第二個引數為請求碼,可以根據業務需求自己編號
                startActivityForResult(new Intent(MainActivity.this, OtherActivity.class), 1);
            }
        });
    }
    
    /**
     * 為了得到傳回的資料,必須在前面的Activity中(指MainActivity類)重寫onActivityResult方法
     * 
     * requestCode 請求碼,即呼叫startActivityForResult()傳遞過去的值
     * resultCode 結果碼,結果碼用於標識返回資料來自哪個新Activity
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        String result = data.getExtras().getString("result");//得到新Activity 關閉後返回的資料
        Log.i(TAG, result);
    }
}

當新Activity關閉後,新Activity返回的資料通過Intent進行傳遞,android平臺會呼叫前面Activity 的onActivityResult()方法,把存放了返回資料的Intent作為第三個輸入引數傳入,在onActivityResult()方法中使用第三個輸入引數可以取出新Activity返回的資料。

使用startActivityForResult(Intent intent, int requestCode)方法開啟新的Activity,新Activity關閉前需要向前面的Activity返回資料需要使用系統提供的setResult(int resultCode, Intent data)方法實現:

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

public class OtherActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.other);

        Button btnClose=(Button)findViewById(R.id.btnClose);
        btnClose.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                //資料是使用Intent返回
                Intent intent = new Intent();
                //把返回資料存入Intent
                intent.putExtra("result", "My name is ting");
                //設定返回資料
                OtherActivity.this.setResult(RESULT_OK, intent);
                //關閉Activity
                OtherActivity.this.finish();
            }
        });
        
    }

}


setResult()方法的第一個引數值可以根據業務需要自己定義,上面程式碼中使用到的RESULT_OK是系統Activity類定義的一個常量,值為-1,程式碼片斷如下:

public class android.app.Activity extends ......{
  public static final int RESULT_CANCELED = 0;
  public static final int RESULT_OK = -1;
  public static final int RESULT_FIRST_USER = 1;
}

PS3:

請求碼的作用  :              

使用startActivityForResult(Intent intent, int requestCode)方法開啟新的Activity,我們需要為startActivityForResult()方法傳入一個請求碼(第二個引數)。請求碼的值是根據業務需要由自已設定,用於標識請求來源。例如:一個Activity有兩個按鈕,點選這兩個按鈕都會開啟同一個Activity,不管是那個按鈕開啟新Activity,當這個新Activity關閉後,系統都會呼叫前面Activity的onActivityResult(int requestCode, int resultCode, Intent data)方法。在onActivityResult()方法如果需要知道新Activity是由那個按鈕開啟的,並且要做出相應的業務處理:。


4、activity的生命週期

1、任務的概念:

任務其實就是activity 的棧它由一個或多個Activity組成的共同完成一個完整的使用者體驗,換句話說任務就是” 應用程式” (可以是一個也可以是多個,比如假設你想讓使用者看到某個地方的街道地圖。而已經存在一個具有此功能的activity 了,那麼你的activity 所需要做的工作就是把請求資訊放到一個Intent 物件裡面,並把它傳遞給startActivity()。於是地圖瀏覽器就會顯示那個地圖。而當用戶按下BACK 鍵的時候,你的activity 又會再一次的顯示在螢幕上,此時任務是由2個應用程式中的相關activity組成的)棧底的是啟動整個任務的Activity,棧頂的是當前執行的使用者可以互動的Activity,當一個activity 啟動另外一個的時候,新的activity 就被壓入棧,併成為當前執行的activity。而前一個activity 仍保持在棧之中。當用戶按下BACK 鍵的時候,當前activity 出棧,而前一個恢復為當前執行的activity。棧中儲存的其實是物件,棧中的Activity 永遠不會重排,只會壓入或彈出,所以如果發生了諸如需要多個地圖瀏覽器的情況,就會使得一個任務中出現多個同一Activity 子類的例項同時存在。

2、任務中的所有activity 是作為一個整體進行移動的。整個的任務(即activity 棧)可以移到前臺,或退至後臺。舉個例子說,比如當前任務在棧中存有四個activity──三個在當前activity 之下。當用戶按下HOME 鍵的時候,回到了應用程式載入器,然後選擇了一個新的應用程式(也就是一個新任務)。則當前任務遁入後臺,而新任務的根activity 顯示出來。然後,過了一小會兒,使用者再次回到了應用程式載入器而又選擇了前一個應用程式(上一個任務)。於是那個任務,帶著它棧中所有的四個activity,再一次的到了前臺。當用戶按下BACK 鍵的時候,螢幕不會顯示出使用者剛才離開的activity(上一個任務的根activity)。取而代之,當前任務的棧中最上面的activity 被彈出,而同一任務中的上一個activity 顯示了出來。

3、Android系統是一個多工(Multi-Task)的作業系統,可以在用手機聽音樂的同時,也執行其他多個程式。每多執行一個應用程式,就會多耗費一些系統記憶體,當同時執行的程式過多,或是關閉的程式沒有正確釋放掉記憶體,系統就會覺得越來越慢,甚至不穩定。

為了解決這個問題, Android 引入了一個新的機制-- 生命週期(Life Cycle)。

Android 應用程式的生命週期是由Android 框架進行管理,而不是由應用程式直接控制。通常,每一個應用程式(入口一般會是一個Activity 的onCreate 方法),都會產生一個程序(Process)。當系統記憶體即將不足的時候,會依照優先順序自動進行程序(process)的回收。不管是使用者或開發者, 都無法確定的應用程式何時會被回收。所以為了很好的防止資料丟失和其他問題,瞭解生命週期很重要。

Activity整個生命週期的4種狀態、7個重要方法

1)   四種狀態

1.      活動(Active/Running)狀態
當Activity執行在螢幕前臺(處於當前任務活動棧的最上面),此時它獲取了焦點能響應使用者的操作,屬於執行狀態,同一個時刻只會有一個Activity 處於活動(Active)或執行

(Running)狀態

1.    暫停(Paused)狀態
當Activity失去焦點但仍對使用者可見(如在它之上有另一個透明的Activity或Toast、AlertDialog等彈出視窗時)它處於暫停狀態。暫停的Activity仍然是存活狀態(它保留著所有的狀態和成員資訊並保持和視窗管理器的連線),但是當系統記憶體極小時可以被系統殺掉

3.      停止(Stopped)狀態

完全被另一個Activity遮擋時處於停止狀態,它仍然保留著所有的狀態和成員資訊。只是對使用者不可見,當其他地方需要記憶體時它往往被系統殺掉

4.      非活動(Dead)狀態

Activity 尚未被啟動、已經被手動終止,或已經被系統回收時處於非活動的狀態,要手動終止Activity,可以在程式中呼叫"finish"方法。

如果是(按根據記憶體不足時的回收規則)被系統回收,可能是因為記憶體不足了

記憶體不足時,Dalvak 虛擬機器會根據其記憶體回收規則來回收記憶體:

      1. 先回收與其他Activity 或Service/Intent Receiver 無關的程序(即優先回收獨

立的Activity)因此建議,我們的一些(耗時)後臺操作,最好是作成Service的形式

      2.不可見(處於Stopped狀態的)Activity

      3.Service程序(除非真的沒有記憶體可用時會被銷燬)

      4.非活動的可見的(Paused狀態的)Activity

      5.當前正在執行(Active/Running狀態的)Activity

2)7個重要方法

當Activity從一種狀態進入另一狀態時系統會自動呼叫下面相應的方

法來通知使用者這種變化

當Activity第一次被例項化的時候系統會呼叫,

整個生命週期只調用1次這個方法

通常用於初始化設定: 1、為Activity設定所要使用的佈局檔案2、為按鈕繫結監聽器等靜態的設定操作

      onCreate(Bundle savedInstanceState);

當Activity可見未獲得使用者焦點不能互動時系統會呼叫

      onStart();

當Activity已經停止然後重新被啟動時系統會呼叫

      onRestart();

當Activity可見且獲得使用者焦點能互動時系統會呼叫

      onResume();

當系統啟動另外一個新的Activity時,在新Activity啟動之前被系統呼叫儲存現有的Activity中的持久資料、停止動畫等,這個實現方法必須非常快。當系統而不是使用者自己出於回收記憶體時,關閉了activity 之後。使用者會期望當他再次回到這個activity 的時候,它仍保持著上次離開時的樣子。此時用到了onSaveInstanceState(),方法onSaveInstanceState()用來儲存Activity被殺之前的狀態,在onPause()之前被觸發,當系統為了節省記憶體銷燬了Activity(使用者本不想銷燬)時就需要重寫這個方法了,當此Activity再次被例項化時會通過onCreate(Bundle savedInstanceState)將已經儲存的臨時狀態資料傳入因為onSaveInstanceState()方法不總是被呼叫,觸發條件為(按下HOME鍵,按下電源按鍵關閉螢幕,橫豎屏切換情況下),你應該僅重寫onSaveInstanceState()來記錄activity的臨時狀態,而不是持久的資料。應該使用onPause()來儲存持久資料。

      onPause();

當Activity被新的Activity完全覆蓋不可見時被系統呼叫

      onStop();

當Activity(使用者呼叫finish()或系統由於記憶體不足)被系統銷燬殺掉時系統呼叫,(整個生命週期只調用1次)用來釋放onCreate ()方法中建立的資源,如結束執行緒等

      onDestroy();

PS4:

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

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

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

4、當前Activity產生事件彈出Toast和AlertDialog的時候Activity的生命週期不會有改變

Activity執行時按下HOME鍵(跟被完全覆蓋是一樣的):onSaveInstanceState --> onPause --> onStop,再次進入啟用狀態時: onRestart -->onStart--->onResume

最後來談一下Activity和Servlet的相似和區別:

 1、有過Web開發經驗的網友對 Servlet的概念應該比較熟悉了。實際上Activity對於Android應用有點類似於Servlet對於Web應用的作用,一個Web應用通常都需要多個Servlet組成,同樣,一個Android應用通常也需要多個Activity組成。對於Web應用而言,Servlet主要負責與使用者互動,並向用戶呈現應用狀態 ;對於Android應用而言,Activity大致也具有相同的功能。

 2、相同點(部分):開發者都無需建立他們的例項,無需呼叫他們的方法,這些由系統以回撥的方式來呼叫,他們的額生命週期由外部進行管理。他們之間不能至今進行相互呼叫,所以不能進行直接的資料交換。

 3、不同點:可以這樣說,一個是B/S模式,一個是C/S模式;Activity以元件來搭建介面,而Servlet則主要以IO流向瀏覽者生成文字響應。