本文主要介绍Activity的启动模式,即"standard"(默认模式)、"singleTop "、"singleTask"、"singleInstance"四大启动模式,在此之前简单总结了一下Android组件的相关知识。
1、Android组件的相关知识
对于Android应用,其应用程序的进程运行方式为:每一个应用程序都运行在它自己的Linux进程中,当应用程序中的任何代码需要执行时,Android将启动进程;当它不在需要且系统资源被其他应用程序请求时,Android将关闭进程。
同时,需要注意的是:Android应用程序不像别的应用程序那样有Main函数入口点,它没有单一的程序入口点,但是它必须要有四个组件中的一个或几个:活动(Activities)、服务(Services)、广播接收者(Broadcast receivers)、内容提供者(Content providers)。
下面分别简单地介绍一下四个组件的概念和作用:
(1)、活动(Activities):一个活动表示一个可视化的用户界面,关注一个用户从事的事件。例如,一个活动可能表示一个用户可选择的菜单项列表,或者可能显示照片连同它的标题。每个活动一起工作形成一个整体的用户界面,但是每个活动是独立于其他活动的。每一个都是作为Activity基类的一个子类的实现。
(2)、服务(Services)、一个服务没有一个可视化用户界面,而是在后台无期限地运行。例如一个服务可能是播放背景音乐而用户做其他一些事情,或者它可能从网络获取数据,或计算一些东西并提供结果给需要的活动(activities)。每个服务都继承自Service基类。
(3)、广播接收者(Broadcast receivers):一个广播接收者是这样一个组件,它不做什么事,仅是接受广播公告并作出相应的反应。许多广播源自于系统代码,例如公告时区的改变、电池电量低、已采取图片、用户改变了语言偏好。应用程序也可以发起广播,例如为了其他程序知道某些数据已经下载到设备且他们可以使用这些数据。
一个应用程序可以有任意数量的广播接收者去反应任何它认为重要的公告。所有的接受者继承自BroadcastReceiver基类。
(4)、内容提供者(Content providers):内容提供者(content provider)使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式。内容提供者继承自ContentProvider 基类并实现了一个标准的方法集,使得其他应用程序可以检索和存储数据。然而,应用程序并不直接调用这些方法。相反,替代的是它们使用一个ContentResolver对象并调用它的方法。ContentResolver能与任何内容提供者通信,它与提供者合作来管理参与进来的进程间的通信。
2、Activity的启动模式(Launch modes)
有四种不同的启动模式可以分配到<activity>元素的launchMody属性: "standard"(默认模式)、"singleTop "、"singleTask"、"singleInstance"。
(1)、"standard"(默认模式)
假设目标task的activity栈为: A->B->C->D(栈底->栈顶),在程序中调用startActivity(D),如果D是standard模式, 则activity栈变为:A->B->C->D->D, 也就是会重新创建一个D实例。
(2)、"singleTop "
假设目标task的activity栈为: A->B->C->D(栈底->栈顶),在程序中调用startActivity(D),如果D是singleTop模式, 则activity栈变为:A->B->C->D,不会创建D实例,就是说如果D在目标task 的activity栈的栈顶,则不会创建新的实例,而是调用D的onNewIntent()方法,反之如果D不在目标task 的activity栈的栈顶,则会重新创建一个D实例。
(3)、"singleTask"
假设目标task的activity栈为: A->B->C->D(栈底->栈顶),如果A调用startActivity(B)启动B实例,则A和B位于同一个task中。
(4)、"singleInstance"
假设目标task的activity栈为: A->B->C->D(栈底->栈顶),如果A调用startActivity(B)启动B实例,则系统会自动给intent添加一个属性FLAG_ACTIVITY_NEW_TASK,将B实例放入到一个新的task中,也就是说A实例所在的task中只能有A自己,不能存在其他实例。
3、验证方法
编程设计两个Activity,一个是MainActivity,另一个是B Activity,在两个Activity中都设置两个按钮,都实现点击返回自身和另一个Activity的功能。在AndroidMainfest.xml中配置launchMode即可实现四种不同的Activity启动模式。
(1)、按钮设计
按钮实现代码如下:
<Button android:textAllCaps="false" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动MainActivity" android:id="@+id/startMainAty" /> <Button android:textAllCaps="false" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动B Activity" android:id="@+id/startBAty" />
(2)、实现两个Activity之间的切换功能
在MainActivity中的实现代码如下:
private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); tv.setText(String.format("TaskID:%d\nMainActivityID:%s",getTaskId(),toString())); findViewById(R.id.startMainAty).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,MainActivity.class)); } }); findViewById(R.id.startBAty).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,BAty.class)); } }); }
在B Activity中的实现代码如下:
private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_baty); tv = (TextView) findViewById(R.id.tv); tv.setText(String.format("TaskID:%d\nB ActivityID:%s",getTaskId(),toString())); findViewById(R.id.startMainAty).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(BAty.this,MainActivity.class)); } }); findViewById(R.id.startBAty).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(BAty.this,BAty.class)); } }); }
(3)、Activity启动模式切换方法
在AndroidMainfest.xml中配置launchMode即可实现四种不同的Activity启动模式。
更改的对应代码如下:
android:launchMode="singleInstance" >
可以配置为:standard(默认方式)/singleTop/singleTask/singleInstance四种方式之一。
4、测试结果
(1)、"standard"(默认模式)
第一个启动的界面如下:
点击“启动MainActivity”,界面为:
点击“启动MainActivity”,界面为:
点击“启动B Activity”,界面为:
点击“启动B Activity”,界面为:
点击“启动MainActivity”,界面为:
测试结果为:任务ID不变,但是每点击一次按钮,实例的ID发生一次变化;B Activity的测试结果也一样,即任务ID不变,但是每点击一次按钮,实例的ID发生一次变化。
结果分析:默认情况下,在同一个任务栈中创建了不同的实例。
应用:可以实现Activity任务之间的跳转机制,原理就是任务栈是先进后出的。
(2)、"singleTop "
第一个启动的界面如下:
点击“启动MainActivity”,界面为:
点击“启动B Activity”,界面为:
点击“启动B Activity”,界面为:
点击“启动MainActivity”,界面为:
测试结果为:点击MainActivity,没有发生变化,说明没有创建新的实例;点击B Activity,实例ID发生变化,说明B Activity创建了新的实例。
结果分析:如果当前Activity处于栈顶,只能创建一个实例;如果当前Activity不处于栈顶,可以创建新的实例。
(3)、"singleTask"
第一个启动的界面如下:
点击“启动MainActivity”,界面为:
点击“启动B Activity”,界面为:
点击“启动B Activity”,界面为:
点击返回键,APP退出。
测试结果为:点击MainActivity,没有发生变化,说明没有创建新的实例;点击B Activity,实例ID发生变化,说明B Activity创建了新的实例,但是在B Activity中点击MainActivity,会发现其实例ID与刚开始的实例ID一致,再次点击返回,APP退出。
结果分析:singleTask使得任务栈中只能存在一个任务,若存在两个Activity,会将之前的Activity自动弹出。
(4)、"singleInstance"
第一个启动的界面如下:
点击“启动MainActivity”,界面为:
点击“启动B Activity”,界面为:
点击“启动B Activity”,界面为:
点击“启动MainActivity”,界面为:
测试结果为:不同的Activity的任务ID不同。
结果分析:一个任务栈中只能有一个Activity,两个任务栈可以互相切换。