android 笔记3

Android实例剖析笔记(三)

上一篇文章 介绍了 Android 的菜单机制,并动手做了一个实验来探究动态菜单的实验机制。这一篇将重点介绍 Activity 的生命周期,通过一个简单的实验来摸索状态转换的机制,最后介绍 NotePad 中使用的自定义控件技术。

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???

android 笔记3_第1张图片
   这里先不讨论例子了,来看看官方文档对 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 类。

作者: phinecos( 洞庭散人 )
出处: http://phinecos.cnblogs.com/

你可能感兴趣的:(android,service,layout,application,文档,button)