上一节的内容是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类。

本文发布:Android开发网
本文地址:http://www.jizhuomi.com/android/example/165.html
2012年8月18日
发布:鸡啄米 分类:Android开发实例 浏览: 评论:1