Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android示例程序剖析之記事本(三:Activity生命周期和自定義控件)

Android示例程序剖析之記事本(三:Activity生命周期和自定義控件)

編輯:Android開發實例

       上一節的內容是Android示例程序剖析之記事本的Android菜單部分,本節主要內容包括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),它們各自有一個button,可以從第一個跳到第二個,也可以從第二個跳回到第一個。配置文件AndroidManifest.xml非常簡單,第二個activity並沒有多余的信息需要指定。

XML/HTML代碼
  1. <application android:icon="@drawable/icon" android:label="@string/app_name">  
  2.         <activity android:name=".frmLogin"  
  3.                   android:label="@string/app_name">  
  4.             <intent-filter>  
  5.                 <action android:name="android.intent.action.MAIN" />  
  6.                 <category android:name="android.intent.category.LAUNCHER" />  
  7.             </intent-filter>  
  8.         </activity>  
  9.          <activity android:name="hello2" android:label="@string/app_name">  
  10.         </activity>  
  11. </application>  

       第一個activity的代碼如下:

Java代碼
  1. public class frmLogin extends Activity    
  2. {   
  3.     private final static String TAG = "FrmLogin";   
  4.   
  5.     /** Called when the activity is first created. */  
  6.     @Override  
  7.     public void onCreate(Bundle savedInstanceState)   
  8.     {   
  9.         super.onCreate(savedInstanceState);   
  10.         Log.v(TAG,"onCreate");   
  11.         setContentView(R.layout.main);   
  12.         this.setViewOneCommand();   
  13.     }   
  14.   
  15.     public void setViewOneCommand()   
  16.     {   
  17.             Button btn = (Button)findViewById(R.id.btnGo);   
  18.             btn.setOnClickListener(new View.OnClickListener()   
  19.             {   
  20.                 public void onClick(View v)   
  21.                 {   
  22.                     Intent intent = new Intent();   
  23.                     intent.setClass(frmLogin.this, hello2.class);   
  24.                     startActivity(intent);   
  25.                     finish();               
  26.                 }   
  27.             });          
  28.             Button btnExit=(Button)findViewById(R.id.btnExit);   
  29.             btnExit.setOnClickListener(new View.OnClickListener()   
  30.             {   
  31.                 public void onClick(View v)   
  32.                 {   
  33.                     frmLogin.this.finish();   
  34.                 }   
  35.             });       
  36.         }    
  37.        
  38.     @Override  
  39.     protected void onDestroy()    
  40.     {   
  41.         super.onDestroy();   
  42.         Log.v(TAG,"onDestroy");   
  43.     }   
  44.   
  45.     @Override  
  46.     protected void onPause()   
  47.     {   
  48.         super.onPause();   
  49.         Log.v(TAG,"onPause");   
  50.   
  51.     }   
  52.   
  53.     @Override  
  54.     protected void onRestart()    
  55.     {   
  56.         super.onRestart();   
  57.         Log.v(TAG,"onRestart");   
  58.     }   
  59.   
  60.     @Override  
  61.     protected void onResume()    
  62.     {   
  63.         super.onResume();   
  64.         Log.v(TAG,"onResume");   
  65.     }   
  66.   
  67.     @Override  
  68.     protected void onStart()    
  69.     {   
  70.         super.onStart();   
  71.         Log.v(TAG,"onStart");   
  72.     }   
  73.   
  74.     @Override  
  75.     protected void onStop()    
  76.     {   
  77.         super.onStop();   
  78.         Log.v(TAG,"onStop");   
  79.     }    
  80. }  

       我在每個onXXX方法中都加入了log方法,值得注意的一點是按鈕單擊事件處理函數中,在最後我調用了finish();待會我會將此行注釋掉進行對比實驗。第二個activity的代碼和第一個完全一樣,只是將setClass的兩個參數反一下,這樣就可以簡單地實現在兩個Activity界面中來回切換的功能了。下面開始實驗,第一個實驗室從第一個activity跳到第二個activity(此時第一個關閉),然後從第二個跳回第一個(此時第二個關閉)。運行後觀察LogCat,得到如下畫面:

Activity生命周期實驗LogCat

       然後來進行第二個實驗,對代碼進行調整,我們把第一個activity中的finish()注釋掉,從第一個activity跳到第二個(此時第一個沒有關閉),然後第二個直接關閉(則第一個會重新來到前端),結果如圖所示,可以看出調用了FrmLogin的onRestart而不是onStart,因為第一個activity只是stop,而並沒有被destory掉。

Activity生命周期實驗LogCat

       前面兩個實驗都很好理解,可第三個實驗就讓我不明白了,過程如下:從第一個activity跳到第二個activity(此時第一個不關閉),然後第二個跳回第一個(此時第二個也不關閉),然後第一個再跳回第二個(此時第一個不關閉),照上面來推斷,應該還是會調用onRestart才對,可實際上它調用的卻是onStart,why???

Activity生命周期實驗LogCat

       這裡先不討論例子了,來看看官方文檔對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控件,它的效果如下圖:

自定義EditText控件

       主要功能就是在文本語句之間繪制分割線。

Java代碼
  1. public static class LinedEditText extends EditText       
  2. {      
  3.       private Rect mRect;      
  4.       private Paint mPaint;      
  5.       // we need this constructor for LayoutInflater      
  6.       public LinedEditText(Context context, AttributeSet attrs)       
  7.       {      
  8.           super(context, attrs);                 
  9.           mRect = new Rect();      
  10.           mPaint = new Paint();      
  11.           mPaint.setStyle(Paint.Style.STROKE);      
  12.           mPaint.setColor(0x800000FF);      
  13.       }          
  14.       @Override      
  15.       protected void onDraw(Canvas canvas)      
  16.       {      
  17.           int count = getLineCount();      
  18.           Rect r = mRect;      
  19.           Paint paint = mPaint;      
  20.           for (int i = 0; i < count; i++)       
  21.           {      
  22.               int baseline = getLineBounds(i, r);      
  23.               canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);      
  24.           }      
  25.           super.onDraw(canvas);      
  26.       }      
  27. }  

       主要工作就是重載onDraw方法,利用從TextView繼承下來的getLineCount函數獲取文本所占的行數,以及getLineBounds來獲取特定行的基准高度值,而且這個函數第二個參數會返回此行的“外包裝”值。再利用這些值繪制這一行的線條。為了讓界面的View使用自定義的EditText類,必須在配置文件中進行設置。

XML/HTML代碼
  1. <view xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     class="com.example.android.notepad.NoteEditor$LinedEditText"  
  3.     android:id="@+id/note"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     android:background="@android:color/transparent"  
  7.     android:padding="5dip"  
  8.     android:scrollbars="vertical"  
  9.     android:fadingEdge="vertical"  
  10.     android:gravity="top"  
  11.     android:textSize="22sp"  
  12.     android:capitalize="sentences"  
  13. />  

       這裡class="com.example.android.notepad.NoteEditor$LinedEditText"就指明了應當使用自定義的LinedEditText類。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved