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

自学Android之Activity:(五)Activity启动模式

2017-03-11 11:51 323 查看
转载请注明出处:http://blog.csdn.net/cc_xz/article/details/61414838

前言:

本篇是Activity部分的最后一篇,我们就主要来聊一聊和Activity启动模式有关的内容。本篇的篇幅可能较长,请通过本篇文章理解不同的启动模式的应用场景,以及了解他们的工作原理。

通过本篇你将了解到:

1.Android任务栈

2.为什么要有Activity启动模式

3.理解四种启动模式的工作原理及使用。

Android任务栈:

在了解Activity的启动模式之前,非常有必要先来聊一聊Android的任务栈。首先我们肯定知道,一个APP也好,一个Android系统也罢,肯定不可能只存在一个界面,也就是说,在APP以及Android系统中会有很多个Activity,那么如何集中管理这些Activity,便是Android任务栈需要考虑的事情。在Android中,会有很多任务栈,它将有关联的Activity集合管理。

通常情况下,同一个APP中的Activity会被放到一个任务栈中;但也可能是不同APP的Activity会被放到一个任务栈中,例如一个程序可以使用QQ或微信登录,这时就本程序的任务栈中就放入了一个QQ或微信的授权界面的Activity;还有一种情况是,同一个APP中的不同的Activity,根据他们的功能和使用场景,开辟不同的任务栈进行管理。

如下图所示:



第一帧中显示了,初始只有一个Activity,并且是存放在默认以APP包名所命名的任务栈中。

第二帧中显示了,在打开TwoActivity后,MainActivity的状态变为了不可见,且被放到了任务栈的底部。而新创建的TwoActivity变为可见状态,且在任务栈的顶部。

第三帧中显示了,再次打开一个新的Activity,之前所打开的Activity又依次被放到了底层中。

第四帧中显示了,又打开一个FourActivity,并且新建了一个任务栈来管理这个Activity,而且原有的Demo03任务栈变为了后台任务栈,里面所有的Activity都变成了不可见的状态。

第五帧中显示了,当又打开Demo03任务栈中的TwoActivity后,自定义任务栈变为了后台任务栈,而且第一个打开的MainActivity仍然被放在任务栈的最底部,对应的,ThreeActivity在任务栈中也下移了。

值得注意的是,只有当APP中的所有任务栈全部关闭后,这个APP才算真正的被销毁,而只有任务栈中的所有Activity被关闭后,这个任务栈才算被销毁。

为什么要有启动模式?

我们知道,在程序中会存在多个Activity,并且相互之间会进行跳转,甚至不同的APP之间也会进行跳转,实际上在上图中显示的流程中,当每次打开一个新的Activity后,无论这个Activity之前是否被创建,都将重新创建这个Activity,那么这样任务栈是没有任何意义的。我们更希望是,如果这个Activity如果已经被创建,我们则直接复用它,如果没有被创建,这时我们再创建它。

因为如果不这样做,之前打开的Activity所耗费的资源(图片、对象等)都得不到释放,而再打开它时又将已有的资源冗余的放到内存中(通过GC来回收),如果你打开了很多Activity,可能还会造成内存溢出而导致程序崩溃。

准备工作

如果你已经看完了我前面的文章,那么相信你对创建Activity、使用Button和TextView已经没有任何问题,所以本篇中的准备工作我并不打算给出具体的代码,而是给出Layout的布局页面,希望你可以通过布局页面自己写出相关的代码。



要注意Button的ID,不要重复。另外,还要记得把对应的组件在Activity中进行初始化。这是个挑战,但关键在于变量名和ID要对应。

另外,你可能做出来的效果不是这个样子,而是只显示了一小部分,这是由于你的布局问题,你只需要打开你的Layout文件,对照下面的代码修改:

错误的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">


正确的:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">


如果你发现Button中显示的字母均为大写,且强迫症犯了的话,可以在Button中添加以下代码解决这个问题:

android:textAllCaps="false"


Standard模式

Standard即标准模式,也是默认的启动模式,在这个模式中,就像前边说的,如果你创建了一个Activity,那么无论这个Activity是否都被创建过,都会重新创建,而不会复用。我们首先通过代码来来验证上述论断,并查看这个模式的特性。

我们首先在StandardActivity中添加如下代码:

Log.e(TAG, "MainActivity的实例号为:" + this.toString());
Log.e(TAG, "MainActivity的返回栈ID为:" + getTaskId());
Log.e(TAG, "MainActivity已启动!");
final Intent intent = new Intent(MainActivity.this, MainActivity.class);


mButtonToStandard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"准备以Standard模式启动Activity");
startActivity(intent);
}
});


mButtonReturn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});


这时启动模拟器,并点击以Standard模式启动,效果如下:



通过Log我们可以发现,每次MainActivity以StandardActivity的方式启动后,实例号都会发现变化,太返回栈却始终唯一,但是当我们销毁当前Activity时,却没有再打印出任何Log。

也就是说,当我们创建了新的Activity后,原有的Activity并没有被销毁(onCreate只有创建的时候才调用。),而是被放到了栈底。这时如果我们选择不断的使用Standard启动新的Activity,终究会得到内存溢出的错误。

如何设置Activity的启动模式

我们知道Activity默认就是使用Standard启动的,所以第一次试验我们并没有手动的设置它的启动模式。其实设置启动模式有两种方式:

1.通过在AndroidManifest.xml文件为Activity指定启动模式,案例代码如下:

<activity android:name=".MainActivity"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>


2.通过在Intent跳转时使用addFlags()来指定启动模式,案例代码如下:

mButtonToSingleTask.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});


第二种方式是通过Intent的Flags的方式启动的。Flags是Android中的标记位,标记位有很多作用,例如设置Activity的启动模式、改变Activity的运行状态等。值得注意的是有些标记位是系统内部使用的,所以程序部要手动去设置这些标记位。下面介绍一下常用的标记位:

FLAG_ACTIVITY_NEW_TASK:该标记位是用于指定Activity通过singleTask模式启动。

FLAG_ACTIVITY_SINGLE_TOP:该标记位是用于指定Activity通过singleTop模式启动。

singleTop模式

栈顶复用模式,在这种模式下,如果有新的Activity一集存在于任务栈的栈顶,那么此Activity如果再次被调用,就不创建新的实例,而是复用已经存在栈顶的Activity。值得注意的是,只有该Activity在栈顶时才会被复用。

下面通过代码来演示效果,添加代码如下:

mButtonToSingleTop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"准备以SingleTop模式启动Activity");
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
});


接着启动模拟器,实现效果如下:



通过图片中可以看到,除了第一次启动(默认标准模式)Activity时弹出的实例信息,当我们点击多次以SingleTop启动Activity时,值弹出了”准备以SingleTop模式启动Activity”的Log,但是并没有弹出实例信息的Log。由此可见,我们并没有新建多个Activity,而是由于此Activity本身已在任务栈的栈顶,所以直接复用了。

这种模式通常比较适用于接收到消息后显示的界面,如qq接收到消息后弹出Activity界面,如果一次来10条消息,总不能一次弹10个Activity,是吧?再比如新闻客户端收到了100个推送,你每次点一下推送他都会进入某个activiy界面(显示新闻只用一个activity,只是内容不同而已),这时也比较适合使用singleTop模式。

singleTask模式

栈内复用,也就是说,如果通过singleTask模式启动,会首先检查本任务栈站内是否存在需要的Activity,如果存在,则将该Activity置于栈顶(复用)。如果不存在,则新建该Activity。

通过输入如下代码,验证以上结论:

mButtonToSingleTask.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"准备以SingleTask模式启动Activity");
intent.setClass(MainActivity.this,TwoActivity.class);
startActivity(intent);
}
});


public class TwoActivity extends AppCompatActivity {
private String TAG = "来自于TwoActivity";
private Button mButtonToSingleTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);

Log.e(TAG, "TwoActivity的实例号为:" + this.toString());
Log.e(TAG, "TwoActivity的返回栈ID为:" + getTaskId());
Log.e(TAG, "TwoActivity已启动!");

mButtonToSingleTask = (Button) findViewById(R.id.TwoButtonToSingleTask);
mButtonToSingleTask.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"准备以SingleTask模式启动MainActivity");
Intent intent = new Intent(TwoActivity.this,MainActivity.class);
startActivity(intent);
}
});
}
}


<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".TwoActivity"
android:launchMode="singleTask"></activity>


敲黑板!注意了!强烈建议使用android:launchMode属性来确定Activity的启动模式,否则会出现一些奇怪的事情。

实现的效果如下:



你可能已经发现了,每当使用MainActivity跳转至TwoActivity时,可以正常的重新调用原有的Activity,但是TwoActivity跳转至MainActivity时,就只能重新创建。这是因为:singTask则是检测整个栈中是否存在当前需要启动的Activity,如果存在就直接将该Activity置于栈顶,并将该Activity以上的Activity都从任务栈中移出销毁。

singleTask 模式比较适合应用的主界面activity(频繁使用的主页面),可以用于主页面的activity(例如新闻、侧滑、应用主界面等)里面包含很多fragment等,一般不会被销毁,它可以跳转其它的activity 界面再回主页面界面,此时其他Activity就销毁了。

singleTask模式

前面三种启动模式,无论活动如何创建、转至前台,其返回栈ID是一致的,而singleinstance的返回栈与其他启动模式的均不相同,它会单独创建一个栈。这样的好处的,如果程序需要同其他程序进行对接(例如在某APP中选择QQ、微信登陆)并不会暴露自身主要的任务栈。所以使用singleinstance模式的活动和类,一般是用于同其他软件进行对接的。

实现代码如下:

mButtonToSingleInstance.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"准备以SingleInstance模式启动TwoActivity");
intent.setClass(MainActivity.this,TwoActivity.class);
startActivity(intent);
}
});


mButtonToSingleInstance = (Button) findViewById(R.id.TwoButtonToSingleInstance);
mButtonToSingleInstance.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"准备以singleInstance模式启动MainActivity");
Intent intent = new Intent(TwoActivity.this,MainActivity.class);
startActivity(intent);
}
});


<activity
android:name=".MainActivity"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".TwoActivity"
android:launchMode="singleInstance">
</activity>


实现效果如下:



在singleInstance模式下,该Activity在整个android系统内存中有且只有一个实例,而且该实例单独使用一个Task。换句话说,A应用需要启动的MainActivity 是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A单独在这个新的任务栈中,如果此时B应用也要激活MainActivity,由于栈内复用的特性,则不会重新创建,而是两个应用共享一个Activity的实例。

后记:

至此,Activity的启动模式已讲解完毕,Activity篇章也将告一段落,后续可能还会补充一些关于原理方面的内容。

不过下一篇将开始讲解Android的基础UI。至此,向各位看官致敬。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息