singleTop启动模式真的可以防止多次打开栈顶的Activity么?

简介: 开发过程中我们经常会遇到各式各样的bug,比如说测试小姐姐告诉我们,由于无操作,某个按钮她`快速点击了两次`(或者由于卡顿之类的延迟),`打开了两个详情页`,希望把这个`禁止掉`,只让打开一个详情页。

singleTop启动模式真的可以防止多次打开栈顶的Activity么?

开发过程中我们经常会遇到各式各样的bug,比如说测试小姐姐告诉我们,由于无操作,某个按钮她快速点击了两次(或者由于卡顿之类的延迟),打开了两个详情页,希望把这个禁止掉,只让打开一个详情页。

快速点击按钮,多次打开默认启动模式的Activity

我们先来复现下这种场景,Activity配置和按钮点击代码很简单,如下所示:
activity配置:

<activity
    android:name=".kfysts.chapter01.activity.LauncherModeSecondActivity"
    android:launchMode="standard" />

按钮的点击事件:

@OnClick(R.id.btn_test1)
public void onBtnTest1Clicked() {
    startActivity(new Intent(this, LauncherModeSecondActivity.class));
}

我们快速点击三下按钮,效果大概如下图所示:

我们看下任务栈信息,在终端中输入如下命令,将数据导入到log.txt中:

不了解日志重定向的同学,请看重定向adb logcat输出到文件.

adb shell dumpsys activity activities > log.txt

打开log.txt,我们看下任务栈信息:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
...
    * TaskRecord{1779ff5 #157 A=com.tiny.demo.firstlinecode U=0 StackId=2 sz=7}
      userId=0 effectiveUid=u0a85 mCallingUid=u0a85 mUserSetupComplete=true mCallingPackage=com.tiny.demo.firstlinecode
      ...
      Activities=[ActivityRecord{991bdbe u0 com.tiny.demo.firstlinecode/.MainActivity t157}, ActivityRecord{9eb4346 u0 com.tiny.demo.firstlinecode/.kfysts.AndroidKfystsActivity t157}, ActivityRecord{d4b56c9 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.AndroidKfystsChapter01Activity t157}, ActivityRecord{ce38d30 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeEntryActivity t157}, ActivityRecord{704de09 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeSecondActivity t157}, ActivityRecord{e063635 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeSecondActivity t157}, ActivityRecord{bd81a7a u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeSecondActivity t157}]
      ...

可以看到我们的LauncherModeSecondActivity打开了三次。

使用singleTop进行防止多次打开

小姐姐提的bug我们不能不管,针对这个bug我们第一反应是做一个防止View的多次点击,不过这里我们有更好的选择。针对点击打开Activity这种操作,可以使用Activity启动模式——singleTop来解决,这种启动模式的核心就是,位于栈顶的Activity不会再次创建实例。

singTop的解释如下:

singleTop:栈顶复用模式。

在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以取出当前请求的信息。

如果新Activity的实例已存在但不是位于栈顶,那么新Activity仍然会重新创建。

话不多说,我们使用singleTop模式来优化下,在AndroidManifest.xml文件中,给对应的Activity添加android:launchMode="singleTop",代码如下:

<activity
    android:name=".kfysts.chapter01.activity.LaunchModeSingleTopTestActivity"
    android:launchMode="singleTop" />

点击启动的代码如下:
这里新增了一条log,我们可以通过log来确定具体点击了几次。

@OnClick(R.id.btn_test5)
public void onBtnTest5Clicked() {
    LogUtils.e("single Top clicked");
    startActivity(new Intent(this, LaunchModeSingleTopTestActivity.class));
}

然后我们尝试多次点击按钮,会发现多次打开Activity的问题已经解决了。

先看下log,我们确实多次点击了按钮:

com.tiny.demo.firstlinecode E: single Top clicked
com.tiny.demo.firstlinecode E: single Top clicked
com.tiny.demo.firstlinecode E: single Top clicked

再看下效果图,如下图:

再来看下具体的任务栈信息,执行adb shell dumpsys activity activities > log.txt,结果如下:

* TaskRecord{b982558 #158 A=com.tiny.demo.firstlinecode U=0 StackId=3 sz=5}
      userId=0 effectiveUid=u0a85 mCallingUid=u0a85 mUserSetupComplete=true mCallingPackage=com.tiny.demo.firstlinecode
      ...
      Activities=[ActivityRecord{47ddc34 u0 com.tiny.demo.firstlinecode/.MainActivity t158}, ActivityRecord{206fabc u0 com.tiny.demo.firstlinecode/.kfysts.AndroidKfystsActivity t158}, ActivityRecord{fa98f6d u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.AndroidKfystsChapter01Activity t158}, ActivityRecord{a41b176 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeEntryActivity t158}, ActivityRecord{e2f82e5 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t158}]

从结果中可以看到,LaunchModeSingleTopTestActivity在栈顶确实只有一个实例。

综合以上的结果,我们可以得出结论,singleTop启动模式确实解决了栈顶Activity重复打开的问题,在多次点击的情况下,栈顶Activity只打开了一次。

singleTop真的能完全防止多次打开栈顶的Activity么?

虽然我们的bug完美解决了,但作为程序员,我们还是需要杠精下的。

这里我改下点击事件的代码,瞬间多次调用启动activity的代码,代码如下:

@OnClick(R.id.btn_test6)
public void onBtnTest6Clicked() {
    for (int i = 0; i < 5; i++) {
        startActivity(new Intent(this, LaunchModeSingleTopTestActivity.class));
    }
}

然后我们看下效果:

纳尼,竟然打开了多个。

再看下任务栈信息:

* TaskRecord{f8d39d #159 A=com.tiny.demo.firstlinecode U=0 StackId=4 sz=9}
      userId=0 effectiveUid=u0a85 mCallingUid=u0a85 
      ...
      Activities=[ActivityRecord{9ed78cc u0 com.tiny.demo.firstlinecode/.MainActivity t159}, ActivityRecord{be3aa3c u0 com.tiny.demo.firstlinecode/.kfysts.AndroidKfystsActivity t159}, ActivityRecord{1654b04 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.AndroidKfystsChapter01Activity t159}, ActivityRecord{a9084f6 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeEntryActivity t159}, ActivityRecord{932877f u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{c4238aa u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{e751011 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{fe1ffe4 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{c0d8413 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}]

可以看到,我们的LaunchModeSingleTopTestActivity确实打开了五次。

其实这也很好理解,不管任何操作都是需要时间去执行的,我们的activity的启动过程也是。

即使我们给Activity设置了启动模式,他们也不是立刻生效的,也需要执行到对应的代码逻辑后才会生效。

所以如果我在for循环里面瞬间执行多次打开Activity的操作,那么启动模式生效的代码还未执行到,所以启动模式就不会生效。

当然了,代码中一般也不会这么写,知其然并知其所以然才是我们的目的。

github项目地址:Android_Base_Demo

具体页面打开路径:

具体页面地址:https://github.com/tinyvampirepudge/Android_Base_Demo/blob/master/app/src/main/java/com/tiny/demo/firstlinecode/kfysts/chapter01/activity/LauncherModeEntryActivity.java

参考

Android开发艺术探索

相关文章
|
15天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
6天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
15 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
41 4
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
30 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
初步认识栈和队列
初步认识栈和队列
58 10
|
1月前
数据结构(栈与列队)
数据结构(栈与列队)
17 1
|
1月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
44 3

热门文章

最新文章