Android執行個體剖析筆記(三)

來源:互聯網
上載者:User

標籤:

摘要:點介紹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執行個體剖析筆記(三)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.