在一个项目中会包含着多个 Activity,系统中使用任务栈来存储创建的 Activity 实例,任务栈是一种 "后进先出" 的栈结构。举个栗子,若我们多次启动同一个 Activity,系统会创建多个实例依次放入任务栈中,当按 back 键返回时,每按一次,一个 Activity 出栈,直到栈空为止,当栈中无任何 Activity,系统就会回收此任务栈。
上面这个例子中的 Activity 并没有设置启动模式,你会发现多次启动同一个 Activity,而系统却创建了多个实例,白白浪费内存,这种情况 Android 早就替我们考虑好了。Android 为 Activity 的创建提供了 4 种启动模式,而根据实际应用场景的不同,为 Activity 选择不同的启动模式,最大化减少了每次都需要在栈中创建一个新的 Activity 的压力,减少内存使用。
启动模式的具体说明和使用场景?下面根据这篇博文来一一解惑。
说明: Android 创建 Activity 时的默认模式,如果没有为 Activity 设置启动模式的话,默认为标准模式。每次启动一个 Activity 都会重新创建一个新的实例入栈,不管这个实例是否存在。
生命周期:如上所示,每次被创建的实例 Activity 的生命周期符合典型情况,它的 onCreate、onStart、onResume 都会被调用。
举例:此时 Activity 栈中以此有 A、B、C 三个 Activity,此时 C 处于栈顶,启动模式为 Standard 模式。若在 C Activity 中添加点击事件,需要跳转到另一个同类型的 C Activity。结果是另一个 C Activity 进入栈中,成为栈顶。
说明:分两种处理情况:需要创建的 Activity 已经处于栈顶时,此时会直接复用栈顶的 Activity,不会再创建新的 Activity;若需要创建的 Activity 不处于栈顶,此时会重新创建一个新的 Activity 入栈,同 Standard 模式一样。
生命周期:若情况一中栈顶的 Activity 被直接复用时,它的 onCreate、onStart 不会被系统调用,因为它并没有发生改变,但是一个新的方法 onNewIntent 会被回调(Activity 被正常创建时不会回调此方法)。
举例:此时 Activity 栈中以此有 A、B、C 三个 Activity,此时 C 处于栈顶,启动模式为 SingleTop 模式。情况一:在 C Activity 中添加点击事件,需要跳转到另一个同类型的 C Activity。结果是直接复用栈顶的 C Activity。情况二:在 C Activity 中添加点击事件,需要跳转到另一个 A Activity。结果是创建一个新的 Activity 入栈,成为栈顶。
说明:若需要创建的 Activity 已经处于栈中时,此时不会创建新的 Activity,而是将存在栈中的 Activity 上面的其它 Activity 全部销毁,使它成为栈顶。
生命周期:同 SingleTop 模式中的情况一相同,只会重新回调 Activity 中的 onNewIntent 方法
举例:此时 Activity 栈中以此有 A、B、C 三个 Activity,此时 C 处于栈顶,启动模式为 SingleTask 模式。情况一:在 C Activity 中添加点击事件,需要跳转到另一个同类型的 C Activity。结果是直接用栈顶的 C Activity。情况二:在 C Activity 中添加点击事件,需要跳转到另一个 A Activity。结果是将 A Activity 上面的 B、C 全部销毁,使 A Activity 成为栈顶。
说明: SingleInstance 比较特殊,是全局单例模式,是一种加强的 SingleTask 模式,它除了具有它所有特性外,还加强了一点:具有此模式的 Activity 只能单独位于一个任务栈中。这个常用于系统中的应用,例如 Launch、锁屏键的应用等等,整个系统中只有一个!所以在我们的应用中一般不会用到,了解即可。
举例:比如 A Activity 是该模式,启动 A 后,系统会为它创建一个单独的任务栈,由于栈内复用的特性,后续的请求均不会创建新的 Activity,除非这个独特的任务栈被系统销毁。
一种静态的指定方法,在 Manifest.xml 文件中声明 Activity 的同时指定它的启动模式,这样在代码中跳转时会按照指定的模式来创建 Activity。例子如下:
- <activity android:name="..activity.MultiportActivity" android:launchMode="singleTask"
- />
一种动态的启动模式,在 new 一个 Intent 后,通过 Intent 的 addFlags 方法去动态指定一个启动模式。例子如下:
- Intent intent = new Intent();
- intent.setClass(context, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
注意:以上两种方式都可以为 Activity 指定启动模式,但是二者还是有区别的。
(1)优先级:动态指定方式即第二种比第一种优先级要高,若两者同时存在,以第二种方式为准。
(2)限定范围:第一种方式无法为 Activity 直接指定 FLAG_ACTIVITY_CLEAR_TOP 标识,第二种方式无法为 Activity 指定 singleInstance 模式。
标记位既可以设定 Activity 的启动模式,如同上面介绍的,在动态指定启动模式,比如 FLAG_ACTIVITY_NEW_TASK 和 FLAG_ACTIVITY_SINGLE_TOP 等。它还可以影响 Activity 的运行状态 ,比如 FLAG_ACTIVITY_CLEAN_TOP 和 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 等。下面介绍几个主要的标记位,切勿死记,理解几个即可,需要时再查官方文档。
作用是为 Activity 指定 "SingleTask" 启动模式,跟在 AndroidMainfest.xml 指定效果相同。
作用是为 Activity 指定 "SingleTop" 启动模式,跟在 AndroidMainfest.xml 指定效果相同。
具有此标记位的 Activity,启动时会将与该 Activity 在同一任务栈的其它 Activity 出栈,一般与 SingleTask 启动模式一起出现。它会完成 SingleTask 的作用,但其实 SingleTask 启动模式默认具有此标记位的作用
具有此标记位的 Activity 不会出现在历史 Activity 的列表中,使用场景:当某些情况下我们不希望用户通过历史列表回到 Activity 时,此标记位便体现了它的效果。它等同于在 xml 中指定 Activity 的属性:
- android:excludeFromRecents="trure"
这四种模式中的 Standard 模式是最普通的一种,没有什么特别注意,而 SingleInstance 模式是整个系统的单例模式,在我们的应用中一般不会应用到,所以,这里就具体讲解 SingleTop 和 SingleTask 模式的运用场景:
最常见的应用场景就是保持我们应用开启后只有一个 Activity 的实例,最典型的例子就是应用中展示的主页(Home 页)。假设用户在主页跳转到其它页面,执行多次操作后想返回到主页,如果不使用 SingleTask 模式,在点击返回的过程中会多次看到主页,这明显就是设计不合理了。
如果你在当前的 Activity 中又要启动同类型的 Activity,此时建议将此类型 Activity 的启动模式指定为 SingleTop,可以减少 Activity 的创建,节省内存!
这里还需要考虑一个 Activity 跳转时携带页面参数的问题。
因为当一个 Activity 设置了 SingleTop 或者 SingleTask 模式后,跳转此 Activity 出现复用原有 Activity 的情况时,此 Activity 的 onCreate 方法将不会再次执行!onCreate 方法只会在第一次创建 Activity 时被执行。
而一般 onCreate 方法中会进行该页面的数据初始化、UI 初始化,如果页面的展示数据无关页面跳转传递的参数,则不必担心此问题,若页面展示的数据就是通过 getInten() 方法来获取,那么问题就会出现:getInten() 获取的一直都是老数据,根本无法接收跳转时传送的新数据!下面,通过一个例子来详解:
- Manifest.xml
- <activity
- android:name=".activity.CourseDetailActivity"
- android:launchMode="singleTop"
- android:screenOrientation="portrait" />
- public class CourseDetailActivity extends BaseActivity{
- ......
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_course_detail_layout);
- initData();
- initView();
- }
- //初始化数据
- private void initData() {
- Intent intent = getIntent();
- mCourseID = intent.getStringExtra(COURSE_ID);
- }
- //初始化UI
- private void initView() {
- ......
- }
- ......
- }
以上代码中的 CourseDetailActivity 在配置文件中设置了启动模式是 SingleTop 模式,根据上面启动模式的介绍可得知,当 CourseDetailActivity 处于栈顶时,再次跳转页面到 CourseDetailActivity 时会直接复用原有的 Activity,而且此页面需要展示的数据是从 getIntent()方法得来,可是 initData() 方法不会再次被调用,此时页面就无法显示新的数据。
当然这种情况系统早就为我们想过了,这时我们需要另外一个回调 onNewIntent(Intent intent)方法,此方法会传入最新的 intent,这样我们就可以解决上述问题。这里建议的方法是重新去 setIntent,然后重新去初始化数据和 UI,代码如下所示:
- /*
- * 复用Activity时的生命周期回调
- */
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- initData();
- initView();
- }
这样,在一个页面中可以重复跳转并显示不同的内容。
启动模式其实是初学 Android 时会学到的知识点,以前也是一知半懂,有些知识点其实跟设计模式一样,你不去使用而只是学习并非可以掌握到精髓,只有真正去使用才会将这些变成你自己的,文章部分内容参考了《Android 开发艺术探索》,好书推荐。
最近在使用启动模式,特此来小小总结一番,网上关于此类的文章也有许多,但是多总结总结还是有益无害的。
欢迎指错~
希望对你们有帮助 :)
来源: http://www.bubuko.com/infodetail-1980686.html