您的位置:首页 > 移动开发 > Android开发

两分钟彻底让你明白Android Activity生命周期(图文)!

2016-05-24 15:51 537 查看


Android开发高手进阶教程


两分钟彻底让你明白Android Activity生命周期(图文)!

大家好,今天给大家详解一下Android中Activity的生命周期,我在前面也曾经讲过这方面的内容,但是像网上大多数文章一样,基本都是翻译Android
API,过于笼统,相信大家看了,会有一点点的帮助 ,但是还不能完全吃透,所以我今天特意在重新总结一下.

首先看一下Android api中所提供的Activity生命周期图(不明白的,可以看完整篇文章,在回头看一下这个图,你会明白的):



 

Activity其实是继承了ApplicationContext这个类,我们可以重写以下方法,如下代码:

[java] view
plaincopy

public class Activity extends ApplicationContext {  

       protected void onCreate(Bundle savedInstanceState);  

         

       protected void onStart();     

         

       protected void onRestart();  

         

       protected void onResume();  

         

       protected void onPause();  

         

       protected void onStop();  

         

       protected void onDestroy();  

   }  

 

为了便于大家更好的理解,我简单的写了一个Demo,不明白Activity周期的朋友们,可以亲手实践一下,大家按照我的步骤来。

第一步:新建一个Android工程,我这里命名为ActivityDemo.

第二步:修改ActivityDemo.Java(我这里重新写了以上的七种方法,主要用Log打印),代码如下:

[java] view
plaincopy

package com.tutor.activitydemo;  

import android.app.Activity;  

import android.os.Bundle;  

import android.util.Log;  

public class ActivityDemo extends Activity {  

     

    private static final String TAG = "ActivityDemo";  

      

    public void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.main);  

          

        Log.e(TAG, "start onCreate~~~");  

    }  

      

    @Override  

    protected void onStart() {  

        super.onStart();  

        Log.e(TAG, "start onStart~~~");  

    }  

      

    @Override  

    protected void onRestart() {  

        super.onRestart();  

        Log.e(TAG, "start onRestart~~~");  

    }  

      

    @Override  

    protected void onResume() {  

        super.onResume();  

        Log.e(TAG, "start onResume~~~");  

    }  

      

    @Override  

    protected void onPause() {  

        super.onPause();  

        Log.e(TAG, "start onPause~~~");  

    }  

      

    @Override  

    protected void onStop() {  

        super.onStop();  

        Log.e(TAG, "start onStop~~~");  

    }  

      

    @Override  

    protected void onDestroy() {  

        super.onDestroy();  

        Log.e(TAG, "start onDestroy~~~");  

    }  

      

}  

 

第三步:运行上述工程,效果图如下(没什么特别的):



核心在Logcat视窗里,如果你还不会用Logcat你可以看一下我的这篇文章 Log图文详解(Log.v,Log.d,Log.i,Log.w,Log.e),我们打开应用时先后执行了onCreate()->onStart()->onResume三个方法,看一下LogCat视窗如下:



BACK键:

当我们按BACK键时,我们这个应用程序将结束,这时候我们将先后调用onPause()->onStop()->onDestory()三个方法,如下图所示:



HOME键:

当我们打开应用程序时,比如浏览器,我正在浏览NBA新闻,看到一半时,我突然想听歌,这时候我们会选择按HOME键,然后去打开音乐应用程序,而当我们按HOME的时候,Activity先后执行了onPause()->onStop()这两个方法,这时候应用程序并没有销毁。如下图所示:



而当我们再次启动ActivityDemo应用程序时,则先后分别执行了onRestart()->onStart()->onResume()三个方法,如下图所示:



 

这里我们会引出一个问题,当我们按HOME键,然后再进入ActivityDemo应用时,我们的应用的状态应该是和按HOME键之前的状态是一样的,同样为了方便理解,在这里我将ActivityDemo的代码作一些修改,就是增加一个EditText。

 

第四步:修改main.xml布局文件(增加了一个EditText),代码如下:

[java] view
plaincopy

<?xml version="1.0" encoding="utf-8"?>  

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:orientation="vertical"  

    android:layout_width="fill_parent"  

    android:layout_height="fill_parent"  

    >  

<TextView    

    android:layout_width="fill_parent"   

    android:layout_height="wrap_content"   

    android:text="@string/hello"  

    />  

<EditText  

    android:id="@+id/editText"  

    android:layout_width="fill_parent"  

    android:layout_height="wrap_content"  

/>  

</LinearLayout>  

第五步:然后其他不变,运行ActivityDemo程序,在EditText里输入如"Frankie"字符串(如下图:)



 

这时候,大家可以按一下HOME键,然后再次启动ActivityDemo应用程序,这时候EditText里并没有我们输入的"Frankie"字样,如下图:



这显然不能称得一个合格的应用程序,所以我们需要在Activity几个方法里自己实现,如下第六步所示:

第六步修改ActivityDemo.java代码如下:

[java] view
plaincopy

package com.tutor.activitydemo;  

import android.app.Activity;  

import android.os.Bundle;  

import android.util.Log;  

import android.widget.EditText;  

public class ActivityDemo extends Activity {  

     

    private static final String TAG = "ActivityDemo";  

    private EditText mEditText;  

    //定义一个String 类型用来存取我们EditText输入的值  

    private String mString;  

    public void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.main);  

        mEditText = (EditText)findViewById(R.id.editText);  

        Log.e(TAG, "start onCreate~~~");  

    }  

      

    @Override  

    protected void onStart() {  

        super.onStart();  

        Log.e(TAG, "start onStart~~~");  

    }  

    //当按HOME键时,然后再次启动应用时,我们要恢复先前状态  

    @Override  

    protected void onRestart() {  

        super.onRestart();  

        mEditText.setText(mString);  

        Log.e(TAG, "start onRestart~~~");  

    }  

      

    @Override  

    protected void onResume() {  

        super.onResume();  

        Log.e(TAG, "start onResume~~~");  

    }  

      

    //当我们按HOME键时,我在onPause方法里,将输入的值赋给mString  

    @Override  

    protected void onPause() {  

        super.onPause();  

        mString = mEditText.getText().toString();  

        Log.e(TAG, "start onPause~~~");  

    }  

      

    @Override  

    protected void onStop() {  

        super.onStop();  

        Log.e(TAG, "start onStop~~~");  

    }  

      

    @Override  

    protected void onDestroy() {  

        super.onDestroy();  

        Log.e(TAG, "start onDestroy~~~");  

    }  

      

}  

第七步:重新运行ActivityDemo程序,重复第五步操作,当我们按HOME键时,再次启动应用程序时,EditText里有上次输入的"Frankie"字样,如下图如示:



OK,大功基本告成,这时候大家可以在回上面看一下Activity生命周期图,我想大家应该完全了解了Activity的生命周期了,不知道你了解了没?


Android Activity生命周期理解误区进行分析

之前看到过一篇文章来自:

http://blog.csdn.net/Android_Tutor/archive/2010/07/28/5772285.aspx

已经全部看完,里面存在部分误区。读者可结合两篇进行阅读。下面就误区作下分析:

 

博友截图:



 

 

 

还有程序中加了几段代码(带注解处加了保存文本的方法处理 ):

 



 

 

    经过我实践分析,这些步骤都是多余的。不用加这两段代码,按HOME键,内容自动保存在EditText中。android内核机制支持这种程序切换引起的数据保存问题(onPause()接口是使用者准备离开activity的地方,在这儿,任何的修改都应该被提交,ContentProvider保存数据,所以不做任何操作,文本会自动保存到CP中。 在onRestart()的时候,不需要重新SET。值仍然会存在,这里大家很容易误解,所以提示一下。)。博友通过代码来保存在某种场合还是比较有用的。在此把这个问题纠正一下。希望读者明白。作者前半部分的生命周期讲的还是很不错的。鼓掌!!!值的大家学习。

 

 

 

 

 

方便理解,特附上生命周期图:

 



 

 

 

结束!


区分Activity的四种加载模式

在多Activity开发中,有可能是自己应用之间的Activity跳转,或者夹带其他应用的可复用Activity。可能会希望跳转到原来某个Activity实例,而不是产生大量重复的Activity。

这需要为Activity配置特定的加载模式,而不是使用默认的加载模式。


加载模式分类及在哪里配置

Activity有四种加载模式:
standard
singleTop
singleTask
singleInstance

设置的位置在AndroidManifest.xml文件中activity元素的android:launchMode属性:

<activity android:name="ActB" android:launchMode="singleTask"></activity>

也可以在Eclipse ADT中图形界面中编辑:





区分Activity的加载模式,通过示例一目了然。这里编写了一个Activity A(ActA)和Activity B(ActB)循环跳转的例子。对加载模式修改和代码做稍微改动,就可以说明四种模式的区别。


standard

首先说standard模式,也就是默认模式,不需要配置launchMode。先只写一个名为ActA的Activity:

package com.easymorse.activities;

import android.app.Activity; 

import android.content.Intent; 

import android.os.Bundle; 

import android.view.View; 

import android.view.View.OnClickListener; 

import android.widget.Button; 

import android.widget.LinearLayout; 

import android.widget.TextView;

public class ActA extends Activity { 

    /** Called when the activity is first created. */ 

    @Override 

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        TextView textView = new TextView(this); 

        textView.setText(this + ""); 

        Button button = new Button(this); 

        button.setText("go actA"); 

        button.setOnClickListener(new OnClickListener() { 

            @Override 

            public void onClick(View v) { 

                Intent intent = new Intent(); 

                intent.setClass(ActA.this, ActA.class); 

                startActivity(intent); 

            } 

        }); 

        LinearLayout layout = new LinearLayout(this); 

        layout.setOrientation(LinearLayout.VERTICAL); 

        layout.addView(textView); 

        layout.addView(button); 

        this.setContentView(layout); 

    } 

}

例子中都没有用layout,免得看着罗嗦。可见是ActA –> ActA的例子。在界面中打印出对象的toString值可以根据hash code识别是否创建新ActA实例。

第一个界面:





点击按钮后:





可以多点几次。发现每次都创建了该Activity的新实例。standard的加载模式就是这样的,intent将发送给新的实例。

现在点Android设备的回退键,可以看到是按照刚才创建Activity实例的倒序依次出现,类似退栈的操作,而刚才操作跳转按钮的过程是压栈的操作。如下图:





singleTop

singleTop和standard模式,都会将intent发送新的实例(后两种模式不发送到新的实例,如果已经有了的话)。不过,singleTop要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不发送给新的实例。

还是用刚才的示例,只需将launchMode改为singleTop,就能看到区别。

运行的时候会发现,按多少遍按钮,都是相同的ActiA实例,因为该实例在栈顶,因此不会创建新的实例。如果回退,将退出应用。





singleTop模式,可用来解决栈顶多个重复相同的Activity的问题。

如果是A Activity跳转到B Activity,再跳转到A Activity,行为就和standard一样了,会在B Activity跳转到A Activity的时候创建A Activity的新实例,因为当时的栈顶不是A Activity实例。

ActA类稍作改动:

package com.easymorse.activities;

import android.app.Activity; 

import android.content.Intent; 

import android.os.Bundle; 

import android.view.View; 

import android.view.View.OnClickListener; 

import android.widget.Button; 

import android.widget.LinearLayout; 

import android.widget.TextView;

public class ActA extends Activity { 

    /** Called when the activity is first created. */ 

    @Override 

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        TextView textView = new TextView(this); 

        textView.setText(this + ""); 

        Button button = new Button(this); 

        button.setText("go actB"); 

        button.setOnClickListener(new OnClickListener() { 

            @Override 

            public void onClick(View v) { 

                Intent intent = new Intent(); 

                intent.setClass(ActA.this, ActB.class); 

                startActivity(intent); 

            } 

        }); 

        LinearLayout layout = new LinearLayout(this); 

        layout.setOrientation(LinearLayout.VERTICAL); 

        layout.addView(textView); 

        layout.addView(button); 

        this.setContentView(layout); 

    } 

}

 

ActB类:

package com.easymorse.activities;

import android.app.Activity; 

import android.content.Intent; 

import android.os.Bundle; 

import android.view.View; 

import android.view.View.OnClickListener; 

import android.widget.Button; 

import android.widget.LinearLayout;

public class ActB extends Activity { 

    @Override 

    protected void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

         Button button=new Button(this); 

            button.setText("go actA"); 

            button.setOnClickListener(new OnClickListener() { 

                @Override 

                public void onClick(View v) { 

                    Intent intent=new Intent(); 

                    intent.setClass(ActB.this, ActA.class); 

                    startActivity(intent); 

                } 

            }); 

            LinearLayout layout=new LinearLayout(this); 

            layout.addView(button); 

            this.setContentView(layout); 

    } 

}

 

ActB类使用默认(standard)加载,ActA使用singleTop加载。结果类似下图:





如果把ActA的加载模式改为standard,情况一样。


singleTask

singleTask模式和后面的singleInstance模式都是只创建一个实例的。

当intent到来,需要创建singleTask模式Activity的时候,系统会检查栈里面是否已经有该Activity的实例。如果有直接将intent发送给它。

把上面singleTop的实例中的ActA的launchMode改为singleTask,ActB的改为standard。那么会发现在ActA界面中按一次按钮:





然后在ActB1界面中按按钮,因为ActA是singleTask,会使用原来的ActA1实例。这时候栈内的情况:





如果多次按按钮跳转,会发现始终只有ActA1这一个ActA类的实例。


singleInstance

解释singleInstance模式比较麻烦。

首先要说一下Task(任务)的概念。

如果是Swing或者Windows程序,可能有多个窗口可以切换,但是你无法在自己程序中复用人家的窗口。注意是直接复用人家的二进制代码,不是你拿到人家api后的源代码级调用。

Android可以做到,让别人的程序直接复用你的Activity(类似桌面程序的窗口)。

Android为提供这种机制,就引入了Task的概念。Task可以认为是一个栈,可放入多个Activity。比如启动一个应用,那么Android就创建了一个Task,然后启动这个应用的入口Activity,就是intent-filter中配置为main和launch的那个(见一个APK文件部署产生多个应用安装的效果)。这个Activity是根(Root)Activity,可能会在它的界面调用其他Activity,这些Activity如果按照上面那三个模式,也会在这个栈(Task)中,只是实例化的策略不同而已。

验证的办法是调用和打印Activity的taskId:

TextView textView2 = new TextView(this); 

textView2.setText("task id: "+this.getTaskId());

会发现,无论切换Activity,taskId是相同的。

当然也可以在这个单一的Task栈中,放入别人的Activity,比如google地图,这样用户看过地图按回退键的时候,会退栈回到调用地图的Activity。对用户来说,并不觉得在操作多个应用。这就是Task的作用。

但是,有这样的需求,多个Task共享一个Activity(singleTask是在一个task中共享一个Activity)。

现成的例子是google地图。比如我有一个应用是导游方面的,其中调用的google地图Activity。那么现在我比如按home键,然后到应用列表中打开google地图,你会发现显示的就是刚才的地图,实际上是同一个Activity。

如果使用上面三种模式,是无法实现这个需求的。google地图应用中有多个上下文Activity,比如路线查询等的,导游应用也有一些上下文Activity。在各自应用中回退要回退到各自的上下文Activity中。

singleInstance模式解决了这个问题(绕了这么半天才说到正题)。让这个模式下的Activity单独在一个task栈中。这个栈只有一个Activity。导游应用和google地图应用发送的intent都由这个Activity接收和展示。

这里又有两个问题:
如果是这种情况,多个task栈也可以看作一个应用。比如导游应用启动地图Activity,实际上是在导游应用task栈之上singleInstance模式创建的(如果还没有的话,如果有就是直接显示它)一个新栈,当这个栈里面的唯一Activity,地图Activity回退的时候,只是把这个栈移开了,这样就看到导游应用刚才的Activity了;
多个应用(Task)共享一个Activity要求这些应用都没有退出,比如刚才强调要用home键从导游应用切换到地图应用。因为,如果退出导游应用,而这时也地图应用并未运行的话,那个单独的地图Activity(task)也会退出了。

如果还是拿刚才的ActA和ActB的示例,可以把ActB的模式改为singleInstance,ActA为standard,如果按一次按钮切换到ActB,看到现象用示意图类似这样:





如果是第一次按钮切换到ActB,在ActB在按按钮切换到ActA,然后再回退,示意图是:





另外,可以看到两个Activity的taskId是不同的。


Android Activity的生命周期

之前也说过了解Android Activity是十分重要的,因为android的应用的生命周期是由android系统决定的!Android Activity生命周期中可认为分为7个阶段;用7个函数原型来描述吧

protected void onCreate(Bundle savedInstanceState)

protected void onStart()

protected void onResume()

protected void onPause()

protected void onStop()

protected void onRestart()

protected void onDestroy()

savedInstanceState对象是用于保存activity的对象的状态,为什么要提到这点呢?看看activity的过程描述吧!

 





每次焦点的变化同时有相应的阶段,这样也是android之所以高效原因之一吧!图上有三个循环,那又代表什么呢?没错,是activity的三个层面!看下图吧!





通过两个图,你会更清楚的了解activity的生命周期了,离开了可见阶段,或者说失去了焦点,activity就很可能被进程终止了!为什么说是可能呢?如果你可过我之前的博文的话,你应该知道android虚拟机的工作原理!

activity主要生命周期的方法说明:
onCreate(BundlesavedInstanceState):创建activity时调用。设置在该方法中,还以Bundle的形式提供对以前储存的任何状态的访问!
onStart():activity变为在屏幕上对用户可见时调用。
onResume():activity开始与用户交互时调用(无论是启动还是重新启动一个活动,该方法总是被调用的)。
onPause():activity被暂停或收回cpu和其他资源时调用,该方法用于保存活动状态的,也是保护现场,压栈吧!
onStop():activity被停止并转为不可见阶段及后续的生命周期事件时调用。
onRestart():重新启动activity时调用。该活动仍在栈中,而不是启动新的活动。
onDestroy():activity被完全从系统内存中移除时调用,该方法被调用可能是因为有人直接调用onFinish()方法或者系统决定停止该活动以释放资源!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  生命周期 android