標籤:
摘要:點介紹Activity的生命週期,通過一個簡單的實驗來摸索狀態轉換的機制
Activity的生命週期
Activity類中有許多onXXX形式的函數可以重載,比如onCreate,onStart,onStop,onPause,那麼它們的調用順序到底是如何的呢?下面就通過一個實驗來進行分析。在做這個實驗之前,我們先得知道如何在Android中進行Log輸出的。我們要使用的是android.util.log類,這個類相當的簡單易用,因為它提供的全是一些靜態方法:
Log.v(String tag, String msg); //VERBOSE
Log.d(String tag, String msg); //DEBUG
Log.i(String tag, String msg); //INFO
Log.w(String tag, String msg); //WARN
Log.e(String tag, String msg); //ERROR
前面的tag是由我們定義的一個標識,一般可以用“類名_方法名“來定義。要在Eclipse中查看輸出的log資訊,需要開啟Logcat(WindowàShow ViewàotheràAndroidàLogCat即可開啟)
實驗一
我們要做的實驗非常簡單,就是有兩個Activity(我這裡分別叫做frmLogin和hello2),t它們各自有一個button,可以從第一個跳到第二個,也可以從第二個跳回到第一個。設定檔AndroidManifest.xml非常簡單,第二個activity並沒有多餘的資訊需要指定。
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".frmLogin"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="hello2" android:label="@string/app_name">
</activity>
</application>
第一個activity的代碼如下:
public class frmLogin extends Activity
{
private final static String TAG = "FrmLogin";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.v(TAG,"onCreate");
setContentView(R.layout.main);
this.setViewOneCommand();
}
public void setViewOneCommand()
{
Button btn = (Button)findViewById(R.id.btnGo);
btn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Intent intent = new Intent();
intent.setClass(frmLogin.this, hello2.class);
startActivity(intent);
finish();
}
});
Button btnExit=(Button)findViewById(R.id.btnExit);
btnExit.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
frmLogin.this.finish();
}
});
}
@Override
protected void onDestroy()
{
super.onDestroy();
Log.v(TAG,"onDestroy");
}
@Override
protected void onPause()
{
super.onPause();
Log.v(TAG,"onPause");
}
@Override
protected void onRestart()
{
super.onRestart();
Log.v(TAG,"onRestart");
}
@Override
protected void onResume()
{
super.onResume();
Log.v(TAG,"onResume");
}
@Override
protected void onStart()
{
super.onStart();
Log.v(TAG,"onStart");
}
@Override
protected void onStop()
{
super.onStop();
Log.v(TAG,"onStop");
}
}
我在每個onXXX方法中都加入了log方法,值得注意的一點是按鈕單擊事件處理函數中,在最後我調用了finish();待會我會將此行注釋掉進行對比實驗。第二個activity的代碼和第一個完全一樣,只是將setClass的兩個參數反一下,這樣就可以簡單地實現在兩個Activity介面中來回切換的功能了。下面開始實驗,第一個實驗室從第一個activity跳到第二個activity(此時第一個關閉),然後從第二個跳回第一個(此時第二個關閉)。運行後觀察LogCat,得到如下畫面:
然後來進行第二個實驗,對代碼進行調整,我們把第一個activity中的finish()注釋掉,從第一個activity跳到第二個(此時第一個沒有關閉),然後第二個直接關閉(則第一個會重新來到前端),結果,可以看出調用了FrmLogin的onRestart而不是onStart,因為第一個activity只是stop,而並沒有被destory掉。
前面兩個實驗都很好理解,可第三個實驗就讓我不明白了,過程如下:從第一個activity跳到第二個activity(此時第一個不關閉),然後第二個跳回第一個(此時第二個也不關閉),然後第一個再跳回第二個(此時第一個不關閉),照上面來推斷,應該還是會調用onRestart才對,可實際上它調用的卻是onStart,why???
這裡先不討論例子了,來看看官方文檔對Activity生命週期的介紹。
1. Android用Activity Stack來管理多個Activity,所以呢,同一時刻只會有最頂上的那個Activity是處於active或者running狀態。其它的Activity都被壓在下面了。
2. 如果非活動的Activity仍是可見的(即如果上面壓著的是一個非全屏的Activity或透明的Activity),它是處於paused狀態的。在系統記憶體不足的情況下,paused狀態的Activity是有可被系統殺掉的。只是不明白,如果它被幹掉了,介面上的顯示又會變成什麼模樣?看來下回有必要研究一下這種情況了。
3. 幾個事件的配對可以比較清楚地理解它們的關係。Create與Destroy配成一對,叫entrie lifetime,在建立時分配資源,則在銷毀時釋放資源;往上一點還有Start與Stop一對,叫visible lifetime,表達的是可見與非可見這麼一個過程;最頂上的就是Resume和Pause這一對了,叫foreground lifetime,表達的了是否處於啟用狀態的過程。
4. 因此,我們實現的Activity衍生類別,要重載兩個重要的方法:onCreate()進行初始化操作,onPause()儲存當前操作的結果。除了Activity Lifecycle以外,Android還有一個Process Lifecycle的說明:
在記憶體不足的時候,Android是會主動清理門戶的,那它又是如何判斷哪個process是可以清掉的呢?文檔中也提到了它的重要性排序:
1. 最容易被清掉的是empty process,空進程是指那些沒有Activity與之綁定,也沒有任何應用程式組件(如Services或者IntentReceiver)與之綁定的進程,也就是說在這個process中沒有任何activity或者service之類的東西,它們僅僅是作為一個cache,在啟動新的Activity時可以提高速度。它們是會被優先清掉的。因此建議,我們的後台操作,最好是作成Service的形式,也就是說應該在Activity中啟動一個Service去執行這些操作。
2. 接下來就是background activity了,也就是被stop掉了那些activity所處的process,那些不可見的Activity被清掉的確是安全的,系統維持著一個LRU列表,多個處於background的activity都在這裡面,系統可以根據LRU列表判斷哪些activity是可以被清掉的,以及其中哪一個應該是最先被清掉。不過,文檔中提到在這個已被清掉的Activity又被重新建立的時候,它的onCreate會被調用,參數就是onFreeze時的那個Bundle。不過這裡有一點不明白的是,難道這個Activity被killed時,Android會幫它保留著這個Bundle嗎?
3. 然後就輪到service process了,這是一個與Service綁定的進程,由startService方法啟動。雖然它們不為使用者所見,但一般是在處理一些長時間的操作(例如MP3的播放),系統會保護它,除非真的沒有記憶體可用了。
4. 接著又輪到那些visible activity了,或者說visible process。前面也談到這個情況,被Paused的Activity也是有可能會被系統清掉,不過相對來說,它已經是處於一個比較安全的位置了。
5. 最安全應該就是那個foreground activity了,不到迫不得已它是不會被清掉的。這種process不僅包括resume之後的activity,也包括那些onReceiveIntent之後的IntentReceiver執行個體。在Android Application的生命週期的討論中,文檔也提到了一些需要注意的事項:因為Android應用程式的生存期並不是由應用本身直接控制的,而是由Android系統平台進行管理的,所以,對於我們開發人員而言,需要瞭解不同的組件Activity、Service和IntentReceiver的生命,切記的是:如果組件的選擇不當,很有可能系統會殺掉一個進行中重要工作的進程。
自訂控制項
這裡主要介紹下“編輯日誌”中使用的一個自訂EditText控制項,它的效果如:
主要功能就是在文本語句之間繪製分割線。
public static class LinedEditText extends EditText
{
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0x800000FF);
}
@Override
protected void onDraw(Canvas canvas)
{
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
for (int i = 0; i < count; i++)
{
int baseline = getLineBounds(i, r);
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
}
super.onDraw(canvas);
}
}
主要工作就是重載onDraw方法,利用從TextView繼承下來的getLineCount函數擷取文本所佔的行數,以及getLineBounds來擷取特定行的基準高度值,而且這個函數第二個參數會返回此行的“外封裝”值。再利用這些值繪製這一行的線條。為了讓介面的View使用自訂的EditText類,必須在設定檔中進行設定
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.example.android.notepad.NoteEditor$LinedEditText"
android:id="@+id/note"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/transparent"
android:padding="5dip"
android:scrollbars="vertical"
android:fadingEdge="vertical"
android:gravity="top"
android:textSize="22sp"
android:capitalize="sentences"
/>
這裡class="com.example.android.notepad.NoteEditor$LinedEditText"就指明了應當使用自訂的LinedEditText類。
Android執行個體剖析筆記(三)