距离上一篇文章,过去有半个多月了,在此期间忙于工作,疏于整理和总结,特此写下这篇博文,来谈谈自己对Activity的理解。总所周知,Activity组件在Android中的重要性不言而喻,我们所能看到的交互动作离不开活动,我们能看到的界面也离不开活动,那么我想从以下几个方面来谈谈:
  • 什么是活动(Activity)
  • 活动(Activity)用法
  • 活动(Activity)生命周期
  • 活动(Activity)启动模式
  • 活动(Activity)管理

1,什么是活动(Activity)

 活动(Activity)是最容易吸引用户的地方了,它是一种可以包含用户界面的组件,主要用于和用户进行交互,一个应用程序可以包含零个或者多个活动。这是我对活动的理解,那我们再看看官方文档是怎么定义的:“An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map.”大概意思是说,Activity是一个可以让屏幕提供用户交互动作的组件,比如打电话、照相、发送邮件和查看地图等。简单来说,我们在应用程序中能看到的内容,绝大多数都是Activity组件提供的。

2,活动(Activity)用法

    在一个应用程序中,有多个Activity组成,其中,有一个特殊的Activity,那就是“main”Activity,代表了应用程序启动,看到的首个界面。每个Activity启动其他的Activity可以通过不同动作。接下来我们来创建一个Activity。
 
  2.1,创建一个Activity
    为了创建一个Activity,我们必须要继承Activity的子类,比如:public class MainActivity extends Activity。同时我们也要重载两个方法onCreate()、onPause(),代码如下:
  
 
  其中onCreate()方法在你的activity创建的时候就调用了,你可以在这个方法中初始化一些常量、资源的连接,其中最重要的是setContentView()方法去加载活动界面的布局。onPause()方法往往是当你活动交互暂停时,一些在界面上填过的交互数据可以在这里保存,以免造成丢失,用户体验不好。
 
  2.2,声明活动,Activity在manifest注册
    除了要定义和继承Activity之外,我们还需要在manifest中进行注册,表示声明,如果不声明的话,这个Activity在运行时候,会报ANR错误,就是应用程序没有响应。那么怎么声明呢,我们每新建一个项目时候,都会有一个AndroidManifest.xml文件,我们需要在AndroidManifest.xml文件中对我们的Activity进行注册。如图
  
  可以看到每一个应用程序,都必须有一个AndroidManifest.xml。
  
  
     可以看到,活动的注册在<application>标签中,这里通过<activity>标签来对活动进行注册的,首先我们使用了android:name属性来具体注册哪一个活动,在这里我们对我们刚才定义的MainActivity类进行注册,由于在最外层的<manifest>标签中已经通过package属性指定了包名是com.example.helloworld,因此在注册活动时这一部分就可以省略了,当然你可以定义为com.example.helloworld.MainActivity,不过直接使用.MainActivity也就足够了,然后我们使用了android:label指定活动中的标题栏的内容,标题栏是显示在活动的最顶部的,需要注意的是,给主活动指定的label不仅会成为标题栏的内容,还会成为启动器(Launcher)中应用程序显示的名称。之后在<activity>标签的内容我们加入了<intent-filter>标签,并在这个标签里添加了<action android:name="android.intent.action.MAIN"/>和<category android:name="android.intent.category.LAUNCHER"/>这两句声明。表示在手机上点击应用图标时,首先启动的就是这个活动,如果你的应用程序中没有声明任何一个活动作为主活动,这个程序仍然是可以正常安装的,只是你无法在启动中看到或者打开这个程序。这种没有主活动的应用程序,基本都是用为第三方服务居多,比如支付宝快捷支付服务。
    基本上以上两个步骤的结合,这个活动就能在应用程序中运行了。

3,活动(Activity)生命周期

  在说说Activity生命周期之前,我们先来理解"Activity返回栈”的概念。在Android中,多个Activity定义在一个Task中,也就是说一个Task是一组Activity的集合,然后Activity又被安排在back stack,即返回栈,是按照后进先出的规则进出栈的。如图:
  
  当从手机中启动一个应用程序图标时,应用程序的Task随之也变成前台进程,如果首次启用时,发现不存在Task实例,那么系统会创建一个Task实例,然后把"main”Activity放入到stack中,默认是栈顶,这样一个Task就管理了一组栈,栈里管理多个Activity。当从Activity1中启动Activity2时,Activity1被推到栈底,Activity2变成栈顶,同理,Activity3进栈的过程和Activity2是一样的,如果我们按back按钮,则Activity3被弹出,系统会根据内存情况进行销售或者回收,Activity2则被推到栈顶,以此类推。当一直按back,返回到主页面,则所有的activitys被全部弹出,则task不复存在。
    同时,task有两种状态:Foreground和Background,前景和背景。当处于Background时,所有的activitys是停止的,当处于Foreground时,表示当前用户交互的应用程序。 
    
 比如两个应用程序A和B,我们刚开始启动了A应用程序,此时Task A是Foreground,用于与用户交互,当我们点击Home Button时,此时Task A变成Background,里面所有的activitys都是停止的,此时如果我们又启动了B应用程序,Task B又会被实例化,Task B变成Foreground,不过Task A仍然是Background,一直等待着被恢复。所以是Android也是一个多任务的系统,不同任务是可以被互相切换的。
    好了关于Task和Stack就先了解这么多,接下来讲讲Activity的生命周期。每个活动在其生命周期中最多可能会有四种状态
  • 运行状态
  • 暂停状态
  • 停止状态
  • 销毁状态
  3.1,运行状态
    当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
 
    3.2,暂停状态
    当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。比如对话框的形式只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动,处于暂停状态的活动仍然是完全存活的,系统也不愿意去回收这种活动,只有在内存极低的情况下,系统才会去考虑回收这种活动。
 
    3.3,停止状态
    当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
 
    3.4,销毁状态
    当一个活动从返回栈中移除后就变成了销毁状态。系统会倾向于回收处于这种状态的活动,从而保证手机的内存充足。
 
    3.5,七个方法
    Activity类中定义了七个回调方法,覆盖了活动生命周期的每一个环节。有如下七个方法:
  • onCreate()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroy()
  • onRestart()
 
    onCreate():每个活动中我们都会重写这个方法,它会在活动第一次被创建的时候调用,比如完成一些初始化操作,加载布局、绑定事件等
    onStart():这个方法在活动不可见变为可见的时候调用
    onResume():这个方法在活动准备好和用户进行交互的时候调用,此时的活动一定位于返回栈的栈顶、并且处于运行状态。
    onPause():这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
    onStop():这个方法在活动完全不可见的时候调用。它和onPause()方法主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法不会执行。
    onDestroy():这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
    onRestart():这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
    
    除此之外,活动又可以分为三种生存期
  • 完整生存期:活动在onCreate()和onDestroy()方法之间所经历的
  • 可见生存期:活动在onStart()和onStop()方法之间所经历的
  • 交互活动期:活动在onResume()和onPause()方法之间所经历的

如图:

  

让我们用代码来体验下Activity的生命周期,首先我们先创建一个类来继承Activity父类,用日志来打印出活动的七个回调方法。定义了两个按钮,一个是启动正常的Activity,一个启动对话框的Activity,布局界面代码就不给出了,然后编写点击事件代码。

 public class MainActivity extends Activity {

     public static final String TAG = "MainActivity";

     @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate"); requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}
Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
startNormalActivity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(intent);
}
});
startDialogActivity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(intent);
}
}); } @Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
} @Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
} @Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
} @Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
} @Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
} @Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
} @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}
}
  首先启动应用程序,默认进来的就是MainActivity界面,点击运行:观察LogCat中的打印日志
  
    可以看到,当MainActivity第一次被运行的时候,会依次执行onCreate()、onStart()和onResume()方法。
    然后点击正常启动另外一个Activity按钮,观察LogCat日志如下:
    
    由于MainActivity完全被另一个Activity覆盖了,因此也执行了onPause()和onStop()方法。
    接着我们从另外一个Activity返回到MainActivity界面,即按下返回按钮,此时观察的LogCat日志如下:
    
    会发现由于之前MainActivity已经进入了停止状态,所以onRestart()方法会得到执行,之后有会依次执行onStart()和onResume()方法。此时要注意,onCreate()方法不会执行,因为MainActivity并没有重新创建。
    然后我们点击启动对话框的按钮,观察LogCat日志如下:
    
    发现只有onPause()方法得到执行,onStop()方法并没有执行,这是因为对话框的Activity并没有完全遮挡住MainActivity,此时MainActivity只是进入了暂停状态,并没有进入停止状态。相应的,按下返回键返回MainActivity也应该只有onResume()方法得到执行。如图:
    
    最后在MainActivity按下返回键,退出程序,活动当然会被销售,执行onDestroy()方法。
    
 

    3.6,活动被回收的情况
    前面说过了,当一个活动进入到停止状态,是有可能被系统回收的,如果我上次执行的临时数据和状态存在,则被回收的话,相当于临时数据被清空了,上次刚刚输入的文字全部都没了,那样的用户体验真是糟糕啊。所以Activity中还提供了一个onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用,因此我们可以通过这个方法来解决会动被回收时临时数据得不到保存的情况。
  

 @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}

 执行以上代码,数据是保存下来了,那么应该在哪里恢复呢,你可以细心的观察到,在onCreate()方法里其实也有一个Bundle类型的参数,这个参数在一般情况下都是null,但是当活动被系统回收之前有通过onSaveInstanceState()方法来保存数据的话,这个参数就会带有之前所保存的全部数据,我们只需要再通过相应的取值方法来将数据取出即可。

     if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}

4,活动(Activity)启动模式

    每个活动(Activity)都有一个相应的启动模式,启动模式一共有四种,分别是standard、singleTop、singleTask、singleInstance,可以在AndroidManifest.xml中通过给<activity>标签指定android:launchMode属性来选择启动模式。
 
  4.1,standard

  standard是活动默认的启动模式,在不进行显示指定的情况下,所有活动都会自动使用这种启动模式。对于返回栈,在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
    如图:
  

    4.2,singleTop
    除了有standard模式之外,还存在一种叫做singleTop的模式,当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
    如图:
    

    4.3,singleTask
    使用singleTop模式很好地解决重复创建栈活动的问题,可是如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。如果想要让某个活动在整个应用程序的上下文中只存在一个实例呢?可以使用singleTask模式来启动。当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
    如图:
    
  
    4.4,singleInstance
    singleInstance模式不同以上三个模式,指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。通常应用以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,那么该如何实现呢?使用前面三种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用了singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
    如图:
    
 
   

5,活动(Activity)管理

    一般我们在日常开发中,基本上会有一个专门的集合类对所有活动进行管理,这样的做的好处是可以保证在退出应用时,活动也可以随时的释放。我们需要定义一个专门管理活动的父类,然后让每个自定义的Activity子类来继承父类,代码如下: 
 public class BaseActivity extends Activity{

     private boolean allowFullScreen=true; //是否允许全屏
private boolean allowDestroy=true; //是否允许销毁 @SuppressWarnings("unused")
private View view; public boolean isAllowFullScreen() { //获取是否全屏
return allowFullScreen;
} public void setAllowFullScreen(boolean allowFullScreen) { //设置是否全屏
this.allowFullScreen = allowFullScreen;
} public boolean isAllowDestroy() { //获取是否销毁
return allowDestroy;
} public void setAllowDestroy(boolean allowDestroy) { //设置是否销毁
this.allowDestroy = allowDestroy;
} public void setAllowDestroy(boolean allowDestroy, View view) { //设置是否销毁(重载)
this.allowDestroy = allowDestroy;
this.view = view;
} @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
allowFullScreen = true;
AppManager.getAppManager().addActivity(this); //添加Activity到堆栈
} @Override
protected void onStop() {
super.onStop();
} @Override
protected void onStart() {
super.onStart();
} @Override
protected void onRestart() {
super.onRestart();
} @Override
protected void onResume() {
super.onResume();
} @Override
protected void onPause() {
super.onPause();
} @Override
protected void onDestroy() {
AppManager.getAppManager().finishActivity(this); // 结束Activity&从堆栈中移除
super.onDestroy();
} @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { //监控返回键,并且包含视图组件
if (!allowDestroy) {
return false;
}
}
return super.onKeyDown(keyCode, event);
}
}

6,总结

    以上就是我对活动(Activity)的理解,理解不到位之处,请园里的各位小伙伴欢迎指出,共同探讨,一起进步。
 

阅读扩展

源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中可以看到技术积累的过程。
 

"浅谈Android"第二篇:活动(Activity)的更多相关文章

  1. "浅谈Android"第一篇:Android系统简介

    近来,看了一本书,名字叫做<第一行代码>,是CSDN一名博主写的,一本Android入门级的书,比较适合新手.看了书之后,有感而发,想来进行Android开发已经有一年多了,但欠缺系统化的 ...

  2. 浅谈android代码保护技术_ 加固

    浅谈android代码保护技术_加固 导语 我们知道Android中的反编译工作越来越让人操作熟练,我们辛苦的开发出一个apk,结果被人反编译了,那心情真心不舒服.虽然我们混淆,做到native层,但 ...

  3. 浅谈Android应用保护(一):Android应用逆向的基本方法

    对于未进行保护的Android应用,有很多方法和思路对其进行逆向分析和攻击.使用一些基本的方法,就可以打破对应用安全非常重要的机密性和完整性,实现获取其内部代码.数据,修改其代码逻辑和机制等操作.这篇 ...

  4. 安卓开发_浅谈Android动画(四)

    Property动画 概念:属性动画,即通过改变对象属性的动画. 特点:属性动画真正改变了一个UI控件,包括其事件触发焦点的位置 一.重要的动画类及属性值: 1.  ValueAnimator 基本属 ...

  5. 浅谈Android应用性能之内存

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 文/ jaunty [博主导读]在Android开发中,不免会遇到许多OOM现象,一方面可能是由于开 ...

  6. 浅谈Android五大布局

    Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦.组件按照布局的要求依次排列,就组成了用户所看见的界面.Android的五大布局分别是LinearLay ...

  7. [转]浅谈Android五大布局(二)——RelativeLayout和TableLayout

    在浅谈Android五大布局(一)中已经描述了LinearLayout(线性布局).FrameLayout(单帧布局)和AbsoulteLayout(绝对布局)三种布局结构,剩下的两种布局Relati ...

  8. [转]浅谈Android五大布局(一)——LinearLayout、FrameLayout和AbsoulteLayout

    Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦.组件按照布局的要求依次排列,就组成了用户所看见的界面.Android的五大布局分别是LinearLay ...

  9. 浅谈Android保护技术__代码混淆

    浅谈Android保护技术__代码混淆   代码混淆 代码混淆(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为.将代码中的各种元 ...

随机推荐

  1. MRBS, meeting room manager system,会议预定管理系统

    MRBS,会议管理软件,新增权限控制,周期性例会管理等. 下载地址 http://www.dotnetcms.org/mrbs/mrbs.rar

  2. 实施vertex compression所遇到的各种问题和解决办法

    关于顶点压缩,好处是可以减少带宽,一定程度提高加载速度,可以提高约5-10%的fps,特别是mobile上,简单描述就是: 压缩之前(32字节) position float3 12normal fl ...

  3. 【转】Android类动态加载技术

    http://www.blogjava.net/zh-weir/archive/2011/10/29/362294.html Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的 ...

  4. XML 和 List 互转类

    XML 和 List 互转类 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  5. C#中将结构类型数据存储到二进制文件中方法

    以往在vb6,vc6中都有现成的方法将结构类型数据写入和读取到二进制文件中,但是在c#中却没有现成的方法来实现,因此我查阅了一些资料,借鉴了网上一些同学的做法,自己写了个类似的例子来读写结构类型数据到 ...

  6. 【实用技巧】取消Win7开机账户的手动选择

    因为前面碰到的一些事情,稍有感慨. 关于win7的一些小技巧都不是什么很有技术含量东西,或者说很浅显.我说一个技巧,也许很多人都知道,也许也早有人说过.但我想说的是我不是在炫耀什么,我只是想分享一些我 ...

  7. 第六章 - 图像变换 - 图像拉伸、收缩、扭曲、旋转[2] - 透视变换(cvWarpPerspective)

    透视变换(单应性?)能提供更大的灵活性,但是一个透视投影并不是线性变换,因此所采用的映射矩阵是3*3,且控点变为4个,其他方面与仿射变换完全类似,下面的例程是针对密集变换,稀疏图像变换则采用cvPer ...

  8. spring整合activemq发送MQ消息[Topic模式]实例

    Topic模式消息发送实例 1.pom引入 <dependency> <groupId>junit</groupId> <artifactId>juni ...

  9. struts2:异常处理

    Struts2框架提供了自己的异常处理机制,只需要在struts.xml文件中配置异常处理即可,而不需要在Action方法中来捕捉异常. 传统方法 public String execute() th ...

  10. 修改windows密码后ssrs报错

    昨夜修改了windows的登录密码,第二日发现ssrs全部无法访问.显示filenotfound等错误.细想一下,应该是修改了windows的密码导致ssrs权限验证失败. 因此将ssrs的服务帐号修 ...