摘要:点介绍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实例剖析笔记(三)的更多相关文章

  1. Android实例剖析笔记(四)

    摘要:分析NoteEditor这个类和以及Content Provider机制 NoteEditor深入分析 首先来弄清楚“日志编辑“的状态转换,通过上篇文章的方法来做下面这样一个实验,首先进入“日志 ...

  2. Android实例剖析笔记(二)

    摘要:用实例讲解Andriod的开发过程,以NotesList为实例介绍Android的菜单机制 简介 android提供了三种菜单类型,分别为options menu,context menu,su ...

  3. Android实例剖析笔记(一)

    摘要:用实例讲解Andriod的开发过程 开卷语 俗话说,“熟读唐诗三百首,不会作诗也会吟”.最近收集了很多Android的示例代码,从这些代码的阅读和实验中学习到很多知识,从而产生写这个系列的打算, ...

  4. 【转】Android实例剖析笔记(二)--用实例讲解Andriod的开发过程,以NotesList为实例介绍Android的菜单机制

    原文网址:http://kb.cnblogs.com/page/78304/ 简介 android提供了三种菜单类型,分别为options menu,context menu,sub menu. op ...

  5. Android Studio 学习笔记(三):简单控件及实例

    控件.组件.插件概念区分 说到控件,就不得不区分一些概念. 控件(Control):编程中用到的部件 组件(Component):软件的组成部分 插件(plugin): 应用程序中已经预留接口的组件 ...

  6. Android Fragement学习笔记(三)----PreferenceFragment的使用

    相信大家对Perference都比較熟悉了,也就是我们常说的偏好设置,首选项设置,能够保存一些数据,比如我们在上一次使用的时候的一些内容,希望在下一次启动后依旧生效,而不须要再进行配置那么麻烦.一般这 ...

  7. Android Fragement学习笔记(三)----PreferenceFragment使用

    我相信每个人都Perference大家都比较熟悉,这就是我们常说的偏好,首选项设置,一些数据可以保存.例如,一些元件的,我们使用上次.希望下次启动后仍然生效,烦.一般这个时候我们便会使用perfere ...

  8. Android自动化学习笔记之MonkeyRunner:官方介绍和简单实例

    ---------------------------------------------------------------------------------------------------- ...

  9. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

随机推荐

  1. windows下mysql配置(第一次)

    忙活了大半天,总算配置好了,本文献给windows下没试用过Mysql的小白,勿喷 http://blog.csdn.net/z1074907546/article/details/51482718 ...

  2. 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]

    题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...

  3. Ubuntu18.04 之jdk安装与环境配置

    1.oracle官网下载压缩包. 下载地址为: https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133 ...

  4. 50个必备jQuery代码段

    0. 如何创建嵌套的过滤器: 1 2 3 4 5 //允许你减少集合中的匹配元素的过滤器, //只剩下那些与给定的选择器匹配的部分.在这种情况下, //查询删除了任何没(:not)有(:has) // ...

  5. Python闭包Closure 2

    由于Python中,变量作用域为LEGB,所以在函数内部可以读取外部变量,但是在函数外不能读取函数内的变量.但是出于种种原因,我们需要读取函数内的变量时候怎么办?那就是在函数内在加一个函数. def ...

  6. Ubuntu下修改为永久DNS的方法

    安装好Ubuntu之后设置了静态IP地址,再重启后就无法解析域名.想重新设置一下DNS,打开/etc/resolv.conf cat /etc/resolv.conf # Dynamic resolv ...

  7. Git 入门使用

    Git是什么? Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制 ...

  8. 【贪心】Google Code Jam Round 1A 2018 Waffle Choppers

    题意:给你一个矩阵,有些点是黑的,让你横切h刀,纵切v刀,问你是否能让切出的所有子矩阵的黑点数量相等. 设黑点总数为sum,sum必须能整除(h+1),进而sum/(h+1)必须能整除(v+1). 先 ...

  9. Hihocoder #1082 : 然而沼跃鱼早就看穿了一切 暴力

    时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 fjxmlhx每天都在被沼跃鱼刷屏,因此他急切的找到了你希望你写一个程序屏蔽所有句子中的沼跃鱼(“marshtomp”,不区 ...

  10. ProtoBuffer使用笔记

    ProtoBuffer是由谷歌研发的对象序列化和反序列化的开源工具,ProtoBuffer和Xml类似,都是数据描述工具,后者使用更为广泛,前者Google内部使用且具有更高的效率.该工具安装和使用都 ...