原网页:点击打开链接

一次关于SingleTask的填坑

这个milestone客户那边做了一个功能,在做这个功能的时候,那边的开发把我们app中的activity的launchmode给改了。之前我们都是采用standard模式的,整个app中维持着一个activity,每次跳屏前会将当前的activity finish掉。下次再进到这个屏,重新执行onCreate,创建这个activity。同时我们有很多初始化UI和数据的代码写在onCreate方法中。现在因为修改了activity的launchmode,导致了每次进到一个屏时,其activity的onCreate方法不一定执行。从而引起了一堆的UI和数据不一致的问题。

在谈到这些问题之前,我们先来自一下activity的launchmode。都知道activity的启动模式有四种,Google推荐我们尽量不要去修改activity的launchmode,对于大多数的应用standard模式就可以适用。这里我们只讲singleTask这一个launchmode.

例子

假设有四个activity。如果采用默认的launchmode, 依次启动 A->B->C->D。栈中将会保留这四个Activity,如果再由D启动A,A启动B, 则栈中顺序会是A->B->C->D->A->B。 这个很好理解。

如果我把B的launchmode 改为singleTask. 依次启动A->B->C->D。栈中仍会保留这四个activity,如果再由D启动A,会发现栈中有5个activity.

采用adb shell dump sys activity命令,我们可以查看当前activity和栈的情况:

Running activities (most recent first):
TaskRecord{423f84e0 #81 A=com.example.fredye.myapplication U=0 sz=5}
Run #5: ActivityRecord{42635c08 u0 com.example.fredye.myapplication/.lanuchmode.ActivityA t81}
Run #4: ActivityRecord{42930618 u0 com.example.fredye.myapplication/.lanuchmode.ActivityD t81}
Run #3: ActivityRecord{430e61d8 u0 com.example.fredye.myapplication/.lanuchmode.ActivityC t81}
Run #2: ActivityRecord{42ee02c0 u0 com.example.fredye.myapplication/.lanuchmode.ActivityB t81}
Run #1: ActivityRecord{427133c8 u0 com.example.fredye.myapplication/.lanuchmode.ActivityA t81}

如果再由A启动B, 此时有意思的事便发生了。同样,我们打印出当前Activity和栈的情况:

Running activities (most recent first):
TaskRecord{423f84e0 #81 A=com.example.fredye.myapplication U=0 sz=2}
Run #2: ActivityRecord{42ee02c0 u0 com.example.fredye.myapplication/.lanuchmode.ActivityB t81}
Run #1: ActivityRecord{427133c8 u0 com.example.fredye.myapplication/.lanuchmode.ActivityA t81}

会发现栈中只有两个activity。我们在ActivityB上指定了launchmode是singleTask,于是在第二次启动ActivityB时,发现当前task里面已经有ActivityB了,便将ActivityB移动栈顶,同时销毁ActivityC和ActivityD。在这个过程中ActivityB的onCreate方法是不会执行的。只会执行其onNewIntent方法。[注意此时所有的Activity都还是在一个task中]

问题来了

1. UI 问题

之前我们的App中每次在Activity跳转的时候都会去finish当前的Activity,确保应用中只存在一个Activity。因此有部份UI的初始化操作是直接放到onCreate方法中做的。singleTask的引入,导致了Activity的onCreate方法不一定执行,因此UI初始化的问题便出现了。

2. 数据不一致问题

如果项目中存在这种代码,那就要小心了

  public class ActivityA extends Activity {private String data;protected void onCreate(Bundle saveInstanceState) {data = (MyApplication)getApplication().getData();}}

对于属性data, 如果在应用中的其它地方对它赋值,会导致在ActivityA中使用的data不是最新的。同样,Root Cause是因为onCreate方法不会每次都执行。

如果碰到采用intent传值也要小心。比如在A activity中有这么一段代码:

Intent intent = new Intent(AActivity.this, BActivity.class);
intent.putExtra("date", new Date().toLocaleString());
startActivity(intent);

由A activity去启动BActivity时用 intent传了一个参数,如果BActivity被设置成了singleTask,由上面的分析,我们知道BActivity中的onCreate方法不一定会执行,因此我们没有在onCreate方法中取intent中的数据,而是从onResume方法里面去取。看起来好像没有问题,但是后来发现,当我们取值的时候,我们发现数据一直都没有更新。此时我们需要进行另外一个操作,重写BActivity中的onNewIntent方法,代码如下:

    //如果当前Activity的启动模式是singleTask, 重定onNewIntent方法可以保证接收到的其它Activity传过来的值是最新的。@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent);}

3. 用户行为的问题

在上面例子中,由于ActivityB设置了launchmode为singleTask, 从而导致了ActivityC和ActivityD被销毁的问题。此时,如果用户按Back键,会发现回退时屏幕出现的顺序和启动的顺序没有匹配上,上面例子中是不能返回到ActivityC屏和ActivityD屏。

关于1, 2 两个问题,当了解了singleTask的引入导致activity的生命周期回调方法的没有按我们预期的执行这个问题,解决起来也就好弄了。关于第3点,用户行为的问题,这个就暂时没有找到比较好的方法去弄了。

官方对singleTask的定义

The system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time. Although the activity starts in a new task, the Back button still returns the user to the previous activity.

实际上我们在使用时发现,对于设置了"singleTask"启动模式的Activity,它在启动的时候,会先检测系统中属性值affinity等于它的属性值taskAffinity的task是否存在;如果存在这样的task,它就会在这个task中启动,否则就开启一个新的task。因此,如果我们想要设置了"singleTask"启动模式的Activity在新的task中启动,就要为它设置一个独立的taskAffinity属性值,taskAffinity默认情况下是应用的包名。下面的代码中,应用程序的包名是com.fred.testactivity,我们将BActivity的启动模式设置成singleTask, 同时设置其taskAffinity属性为com.fred.testactivity.BActivity 。当BActivity启动时,会发现多了一个task 。代码如下:

<activityandroid:name=".BActivity"android:label="@string/title_activity_b"android:launchMode="singleTask"android:taskAffinity="com.fred.testactivity.BActivity">
</activity>

补充:android中的task和stack

首先我们需要明白任何一个Android app是由多个Activity组成的. 每一个Activity可以启动自己app应用中的activity或者其它app应用中的Activity.每一个Activity可以声明它所要处理的意图。当声明处理同一个意图的Activity有多个时,系统就会弹出一个窗口让用户进行选择。

一个task便是一系列Activity的集合。当用户在APP A中发出了一个发送邮件的意图,系统打开了邮件客户端,邮件发送完后,又回到了APP A, 给用户的感觉发送邮件就是在APP A中完成的一样。这个便是由于发送邮件的Activity, 和APP A中的activity在同一个Task中。Task中的activity是交给一个stack来管理的,stack中activity顺序是按照它们启动的先后顺序。

桌面是大多数task启动的位置,当用户点击了一个app 的 icon时,这个app 的task便由后台转到前台。如果当前的app 没有对应的task, 便创建一个新的task,同时启动它的”main” activity,同时将其放入stack中。如果当前的activity启动另外一个activity, 新的activity便会放到栈顶,之前的activity会被stop, 系统会保留它的状态。当用户点击Back键时,当前activty会出栈(会被destory, 它的onDestory方法会被调用),它的前一个activity会被Resume。如果用户不断的按Back键,该Task中的Activity将会一个接一个的出栈,当这个栈中的activity都出栈了,这个task也就不存在了。

Google的提醒

我们可以通过设置launchmode和affinity来管理Task, 但Google给了我们一个提醒 --大多数应用不要改变Activity的启动模式, 原文如下

Caution: Most applications should not interrupt the default behavior for activities and tasks. If you determine that it's necessary for your activity to modify the default behaviors, use caution and be sure to test the usability of the activity during launch and when navigating back to it from other activities and tasks with the Back button. Be sure to test for navigation behaviors that might conflict with the user's expected behavior.

一次关于SingleTask的填坑相关推荐

  1. java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得

    该文章出自:http://www.cnblogs.com/hucn/p/3572384.html 分析工具:http://www.blogjava.net/jjshcc/archive/2014/03 ...

  2. 20150726 填坑日记

    三中内填坑: 1. 组合数递推什么的 C(m,n)=C(m,n-1)+C(m-1,n-1).填了个大坑,以前没认真听课QAQ 2. 裸题过河卒 3. 缺角正方形摆放车统计,分上下部分,枚举上部分放几个 ...

  3. 传统行业转型微服务的挖坑与填坑

    原文:传统行业转型微服务的挖坑与填坑 一.微服务落地是一个复杂问题,牵扯到IT架构,应用架构,组织架构多个方面 在多家传统行业的企业走访和落地了微服务之后,发现落地微服务是一个非常复杂的问题,甚至都不 ...

  4. 开发工具总结(4)之Android Studio3.0填坑指南

    序言 Android Studio 3.0 上篇讲了: 全面总结Android Studio2.X的填坑指南 这篇讲一下AS3.0的坑.. 作为这个世界上走在最前沿的生物"猿",怎 ...

  5. 【结果很简单,过程很艰辛】记阿里云Ons消息队列服务.NET接口填坑过程

    Maybe 这个问题很简单,因为解决方法是非常简单,但填坑过程会把人逼疯,在阿里云ONS工作人员.同事和朋友的协助下,经过一天的调试和瞎捣鼓,终于解决了这个坑,把问题记下来,也许更多人在碰到类似问题的 ...

  6. Android Studio 填坑指南

    前几天发布了一篇名为<Android Studio 安装.配置及第一个程序演示>的博文,有不少童鞋都认真阅读过并照步骤一步一步操作了一遍,有滴如期成功地装好了,然鹅有滴反映说这不对呀,为森 ...

  7. 即将上线的Hive服务器面临的一系列填坑笔记

    即将上线的Spark服务器面临的一系列填坑笔记 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.18/10/19 16:36:31 WARN metastore.ObjectSt ...

  8. 第二篇:salt-api使用填坑指南

    前言 salt-api在使用时,有些坑欲哭无泪,一路趟雷过后,总结了一趟salt-pai使用填坑指南,保君一路畅通. salt-api 安装 #本文的安装版本 2018.3.2-1.el6 yum i ...

  9. React Native填坑之旅--动画篇

    React Native填坑之旅--Button篇 React Native填坑之旅--动画 React Native填坑之旅--HTTP请求篇 动画是提高用户体验不可缺少的一个元素.恰如其分的动画可 ...

最新文章

  1. 形式参数内存在哪java_深入浅出Java中JVM内存管理
  2. (字符串)统计字母和数字个数,打印柱状图
  3. CSS3自定义滚动条
  4. golang中的栈(LeetCode刷题)
  5. java - 判断任意一天是这年的第几天
  6. java future接口_java Future 接口介绍
  7. Linux查看、运行和杀死java进程
  8. 简单电子相册视频制作的步骤和要点
  9. Unity 自定义Log系统
  10. 如何在WEBLOGIC中设置日志输入
  11. 6.UNIX 环境高级编程--系统数据文件和信息
  12. 腾讯QQ表情生意经:建开放平台 与原创者最高六四分成
  13. 选择 ASP.NET Core Web UI--主线
  14. 5号字对应的数字字号_字号对照表
  15. 即时通讯软件七大优势详解
  16. matlab半周期正弦拟合,Matlab:帮助理解正弦曲线拟合
  17. sql触发器Tigger
  18. 女朋友转行 Java 失败,被辞退了。。。
  19. 计算机上的钢筋检测数据,梁板钢筋面积查询软件
  20. Springboot教师教学质量评价系统

热门文章

  1. Alfresco 4.0安装手把手教程
  2. Ubuntu 16.04 tf-faster-rcnn 在CPU下训练数据
  3. 系统总线【计算机组成原理】
  4. PostGIS教程五:数据
  5. 神经网络学习9--过拟合 (Overfitting)的解决办法
  6. 云计算中存储基础知识
  7. 【机器学习】数据归一化全方法总结:Max-Min归一化、Z-score归一化、数据类型归一化、标准差归一化等
  8. bash alias命令03
  9. 49天精通Java,第13天,java字符串简介
  10. python--剑指offer--14- I. 剪绳子