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

Activity的生命周期

Activity类中有许多onXXX形式的函数可以重载,比如onCreate,onStart,onStop,onPause,那么它们的调用顺序到底是如何的呢?下面就通过一个实验来进行分析。在做这个实验之前,我们先得知道如何在Android中进行Log输出的。

我们要使用的是android.util.log类,这个类相当的简单易用,因为它提供的全是一些静态方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> Log.v(Stringtag,Stringmsg); // VERBOSE
Log.d(Stringtag,Stringmsg); // DEBUG
Log.i(Stringtag,Stringmsg); // INFO
Log.w(Stringtag,Stringmsg); // WARN
Log.e(Stringtag,Stringmsg); // ERROR

前面的tag是由我们定义的一个标识,一般可以用类名_方法名来定义。要在Eclipse中查看输出的log信息,需要打开LogcatWindowàShow ViewàotheràAndroidàLogCat即可打开)

实验一

我们要做的实验非常简单,就是有两个Activity(我这里分别叫做frmLoginhello2,t它们各自有一个button,可以从第一个跳到第二个,也可以从第二个跳回到第一个。

配置文件AndroidManifest.xml非常简单,第二个activity并没有多余的信息需要指定。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> < 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的代码如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> public class frmLogin extends Activity
{
private final static StringTAG = " FrmLogin " ;

/** Calledwhentheactivityisfirstcreated. */
@Override
public void onCreate(BundlesavedInstanceState)
{
super .onCreate(savedInstanceState);
Log.v(TAG,
" onCreate " );
setContentView(R.layout.main);
this .setViewOneCommand();
}

public void setViewOneCommand()
{
Buttonbtn
= (Button)findViewById(R.id.btnGo);
btn.setOnClickListener(
new View.OnClickListener()
{
public void onClick(Viewv)
{
Intentintent
= new Intent();
intent.setClass(frmLogin.
this ,hello2. class );
startActivity(intent);
finish();
}
});
ButtonbtnExit
= (Button)findViewById(R.id.btnExit);
btnExit.setOnClickListener(
new View.OnClickListener()
{
public void onClick(Viewv)
{
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跳到第二个(此时第一个没有关闭),然后第二个直接关闭(则第一个会重新来到前端),结果如图所示,可以看出调用了FrmLoginonRestart而不是onStart,因为第一个activity只是stop,而并没有被destory掉。

前面两个实验都很好理解,可第三个实验就让我不明白了,过程如下:从第一个activity跳到第二个activity(此时第一个不关闭),然后第二个跳回第一个(此时第二个也不关闭),然后第一个再跳回第二个(此时第一个不关闭),照上面来推断,应该还是会调用onRestart才对,可实际上它调用的却是onStartwhy???


这里先不讨论例子了,来看看官方文档对Activity生命周期的介绍。

1.AndroidActivity Stack来管理多个Activity,所以呢,同一时刻只会有最顶上的那个Activity是处于active或者running状态。其它的Activity都被压在下面了。

2.如果非活动的Activity仍是可见的(即如果上面压着的是一个非全屏的Activity或透明的Activity),它是处于paused状态的。在系统内存不足的情况下,paused状态的Activity是有可被系统杀掉的。只是不明白,如果它被干掉了,界面上的显示又会变成什么模样?看来下回有必要研究一下这种情况了。

3.几个事件的配对可以比较清楚地理解它们的关系。CreateDestroy配成一对,叫entrie lifetime,在创建时分配资源,则在销毁时释放资源;往上一点还有StartStop一对,叫visible lifetime,表达的是可见与非可见这么一个过程;最顶上的就是ResumePause这一对了,叫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列表,多个处于backgroundactivity都在这里面,系统可以根据LRU列表判断哪些activity是可以被清掉的,以及其中哪一个应该是最先被清掉。不过,文档中提到在这个已被清掉的Activity又被重新创建的时候,它的onCreate会被调用,参数就是onFreeze时的那个Bundle。不过这里有一点不明白的是,难道这个Activitykilled时,Android会帮它保留着这个Bundle吗?

3.然后就轮到service process了,这是一个与Service绑定的进程,由startService方法启动。虽然它们不为用户所见,但一般是在处理一些长时间的操作(例如MP3的播放),系统会保护它,除非真的没有内存可用了。

4.接着又轮到那些visible activity了,或者说visible process。前面也谈到这个情况,被PausedActivity也是有可能会被系统清掉,不过相对来说,它已经是处于一个比较安全的位置了。

5.最安全应该就是那个foreground activity了,不到迫不得已它是不会被清掉的。这种process不仅包括resume之后的activity,也包括那些onReceiveIntent之后的IntentReceiver实例。

Android Application的生命周期的讨论中,文档也提到了一些需要注意的事项:因为Android应用程序的生存期并不是由应用本身直接控制的,而是由Android系统平台进行管理的,所以,对于我们开发者而言,需要了解不同的组件ActivityServiceIntentReceiver的生命,切记的是:如果组件的选择不当,很有可能系统会杀掉一个正在进行重要工作的进程。

自定义控件

这里主要介绍下编辑日志中使用的一个自定义EditText控件,它的效果如下图:

主要功能就是在文本语句之间绘制分割线。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> public static class LinedEditText extends EditText
{
private RectmRect;
private PaintmPaint;
// weneedthisconstructorforLayoutInflater
public LinedEditText(Contextcontext,AttributeSetattrs)
{
super (context,attrs);
mRect
= new Rect();
mPaint
= new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(
0x800000FF );
}
@Override
protected void onDraw(Canvascanvas)
{
int count = getLineCount();
Rectr
= mRect;
Paintpaint
= 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类,必须在配置文件中进行设置

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> < 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类。

作者:洞庭散人

出处:http://phinecos.cnblogs.com/    

本博客遵从 Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由转载,但请保留原作者信息和文章链接URL。

更多相关文章

  1. android中URI简介
  2. Android多媒体开发(4)——添加FFmpeg支持
  3. Android(安卓)NDK开发:JNI基础篇
  4. 【Android(安卓)XML】Android(安卓)XML 转 Java Code 系列之 sty
  5. 那两年炼就的Android内功修养
  6. MediaStore与Media.EXTERNAL_CONTENT_URI
  7. Android(安卓)app——常见控件的使用方法
  8. Android之如何打开USB调试模式
  9. android使用ExpandableListView控件实现小说目录效果的例子

随机推荐

  1. Android(安卓)Web App官方文档翻译第一章
  2. Android Button及TextView动态变换颜色
  3. Android8.1 Launcher3 去掉抽屉(三)
  4. Android横竖屏常问题
  5. 【Android】内存泄漏调试
  6. IcedRobot——基于OpenJDK的Android分支
  7. android工作小结 ---- 1
  8. 【Android】ViewPager实现图片左右滑动播
  9. Android中Timer类和TimerTask粗解
  10. android基础之页面间跳转传值,用Bundle+I