2011-05-04

Android Activity 筆記

前言
  • 一個 Android 應用程式由數個 activity 組成,每個 activity 代表一個畫面,該畫面一般與螢幕等大,也可以小於螢幕。
  • 每個應用程式都會有一個名為 main 的 activity,用來啟動該應用程式。
  • 每個 activity 可以啟動另一個 activity,當子 activity 啟動後,父 activity 會暫停執行(onPause),直到子 activity 因使用者按下 Back 而關閉後(onPause, onStop, onDestroy)才會重新執行(onResume)。
  • Android 以堆疊的方式(last in, first out)實做 activity 間的關係,也就是子 activity 疊在父 activity 上,孫 activity 又疊在子 activity 上。
建立 activity
  • 繼承 Activity 或其子類別。
  • 實做因 activity 狀態改變而觸發的 callback method,最重要的就是 onCreate 與 onPause。
  • onCreate 負責初始化必要的物件與呼叫 setContentView 設定畫面。
  • onPause 表示 user 暫時離開或永久離開,要將該存的資料儲存起來。
  • Activity class 完成後,得在 AndroidManifest.xml 定義該 activity,<activity> 有很多像是 label 或者 icon 的屬性可以設定,請參考 這裡
    <manifest ... >
        <application ... >
            <activity android:name=".ExampleActivity" />
            ...
        </application ... >
        ...
    </manifest >
  • <activity> 可以使用 <intent-filter> 來設定 activity 的啟動方式,每個 Android 應用程式預設都會有以下的 <intent-filter> 設定值,第一個設定值表示該 activity 為應用程式的入口,第二個設定值表示 user 可以啟動該 activity,每個應用程式最多只有一個 activity 可以有以上的設定值,<intent-filter> 的詳細資訊請看 這裡
    <activity ...>
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
  • 不想對其他應用程式開放的 activity 就不要定義 <intent-filter>,沒有定義 <intent-filter> 的 activity 只能給自身的應用程式使用。
啟動 activity
  • 可以使用 startActivity() 啟動 main activity 以外的 activity,傳入 Intent 來指定要啟動的 activity,不限制同一個應用程式裡的 activity,也可以透過 Intent 傳送資料過去。
    startActivity(new Intent(this, ExampleActivity.class));
  • 若要透過 Intent 啟動其他應用程式的 activity,例如寄送 email 或傳送簡訊,這時因為不知道實際的 activity 名稱,可以用 action 代替,Android 會依據 action 去找出適合的 activity 來執行,甚至當同一的 action 有多個 activity 時,Android 會讓 user 做選擇,呼叫外部程式是 Intent 真正厲害的地方。
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
    startActivity(intent);
  • 有時候要從新 activity 裡取得資料,那可以改用 startActivityForResult() 來啟動 activity,並實做 onActivityResult 來取得回傳的資料。
    private void pickContact() {
        Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
        startActivityForResult(intent, PICK_CONTACT_REQUEST);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
            Cursor cursor = getContentResolver().query(data.getData(),
            new String[] {Contacts.DISPLAY_NAME}, null, null, null);
            if (cursor.moveToFirst()) {
                int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
                String name = cursor.getString(columnIndex);
                // ...
            }
        }
    }
結束 activity
  • 可以呼叫 finish() 結束本身的 activity,也可以呼叫 finishActivity(requestCode) 結束剛啟動的另一個 activity(怎會有這種機會?)。
  • 一般來說,這兩個 method 是用不到的,因為 Android 會自動管理所有的 activity 的生命週期。
Activity 生命週期
  • Activity 有三種狀態:Resumed、Paused 與 Stopped。
    • Resumed: 位於畫面最上層,user 使用中,也可以稱為 Running。
    • Paused:被另一個 activity 蓋過去,也許還可以看到畫面,只要上面的 activity 沒有佔滿整個畫面或者使用透明背景的話,這時 activity 還是活著的,只是沒有在動,當系統記憶體不足時有可能會被殺掉。
    • Stopped:完全看不到,但還是活的,隨時可能被系統殺掉。
  • 當 activity 處於 Paused 或 Stopped 狀態時,系統可能透過 finish() 或者直接殺掉 process 來結束他的生命,所以當進入 Paused 或 Stopped 狀態時,要先將後事交待好。
  • 所有 callback method 的執行時機:
    • onCreate:建立 activity 時。
    • onStart:activity 置於畫面最上層時。
    • onResume:activity 回到畫面最上層時。
    • onPause:被另一個 activity 蓋住時。
    • onStop:完全看不到 activity 時。
    • onDestroy:activity 即將被殺掉時。
  • 實做上述所有 callback method 時,一定要先呼叫相對應的 super.onXXX()。
  • 三種生命週期:
    • 出生到死亡:即從 onCreate() 到 onDestroy(),分別只會被執行一次,onCreate() 要初始化所有要用的物件,包括 layout view,onDestroy() 要釋放所有資源。
    • 可見的狀態:從 onStart() 到 onStop(),分別負責啟動與停止畫面上的動作,不一定是 focus 狀態,可能被其他 activity 蓋住,可以被執行多次。
    • 前景的狀態:從 onResume() 到 onPause(),user 使用中,因為這兩個 method 會被頻繁的執行,所以不要在裡面寫太麻煩的 code。
    • 上圖最重要的一點:onResume() 不只在 onPause() 之後執行,在第一次啟動 activity 時,也會在 onStart() 之後執行 onResume()。
  • 系統可能在 onPause()、onStop() 或 onDestroy() 執行後,因為記憶體需要,隨時殺掉該 activity,而 onPause() 是三個 method 裡面第一個會被執行的 method,在系統可能在尚未執行 onStop() 或 onDestroy() 前殺掉 activity,所以 onPause() 裡要將必要的資料寫回資料庫,以及停止動畫與停止消耗 CPU 的動作,但要小心 onPause() 不可以執行太久。
  • onSaveInstanceState()
    • 當 activity 被系統殺掉後,如果 user 再回到該 activity,系統會重新建立該 activity,但是 user 並不知道 activity 發生什麼事了,所以 user 會期望回復到離開前的樣子,這時就要實做 onSaveInstanceState() 來保留 user 走過的痕跡到 Bundle 物件裡,然後系統在重新建立 activity 時,會將該 Bundle 物件傳給 onCreate(),就這樣,像是什麼事都沒發生過一樣。
    • onSaveInstanceState() 多半在 onStop() 前呼叫,但也可能在 onPause() 前呼叫,但不一定總是會呼叫,因為當 user 按下 BACK 時,表示 user 知道要離開 activity 了,那就沒有必要呼叫 onSaveInstanceState()。
    • 並不一定要實做 onSaveInstanceState(),因為像是 EditText 或 CheckBox 之類的 View 元件有預設的 onSaveInstanceState() 實做,元件只要加上 android:id 屬性就會保留狀態了,換句話說,如果沒有加 android:id 屬性,則表示不想要保留狀態,也可以用 android:saveEnabled 或者 setSaveEnabled() 來強制 View  元件不要保留狀態。
    • 那 onSaveInstanceState() 裡到底要做什麼?activity 裡的 member values,但是不要跟 onPause() 裡紀錄到資料庫的資料搞混,前者只是紀錄 user 走過的痕跡,後者紀錄的是永久的資料。
    • 記得要呼叫 super.onSaveInstanceState(),不然 View 元件就會呆掉了。
    • 要怎麼測試 onSaveInstanceState() 呢?轉 90 度就可以了,因為要套用新的 layout,所以系統會將 activity 殺掉再重新建立。
  • 系統執行時動態改變設定值,如轉 90度、開啟鍵盤或者改變語系,都會促使系統殺掉再重新建立 activity,所以一定要實做 onSaveInstanceState() 與 onRestoreInstanceState(或者 onCreate())。 
  • 當前後兩個 activity 有共用資料或資源時,要注意兩個 activity 的執行順序,最重要的是共用資源得在 onPause() 裡關閉,不可以在 onStop() 裡
    • aActivity.onPause()
    • bActivity.onCreate()
    • bActivity.onStart()
    • bActivity.onResume()
    • aActivity.onStop() - 如果看不到 aActivity 的話,才會執行

沒有留言:

張貼留言