关于intent.setFlags()的一些故事

今天项目中遇到了几个坑,环环相扣,扣人心弦。感觉蛮有意思的,纪录一下。

坑一:奇葩需求

首先需要解决的一个问题如下:

Activity A 以startActivityForResult()方法启动了Activity B,然后Activity B可以启动Activity C,Activity C可以启动Activity D。需要在B、C和D都可以通过setResult()方法将数据返回给A。

这种需求估计不常见,解决这个问题其实并不困难,最容易想到一个方法,就是将C和D都通过startActivityForResult()方式来启动,但是我一直在想有没有其他更简单的方式解决这个问题,毕竟太懒了,不想每个Activity都走一遍这个方法。

网上查了一下,发现有个好方法可以解决我的问题:在每个Activity(C、D)启动时,为intent设置一个Intent.FLAG_ACTIVITY_FORWARD_RESULT,这样,无论用户在C、D界面准备退出时,都可以通过setResult()给A返回数据。

FLAG_ACTIVITY_FORWARD_RESULT:

If set and this intent is being used to launch a new activity from an existing one, then the reply target of the existing activity will be transfered to the new activity.

如果设置,并且该intent会启动一个新的Activity,则返回消息的目标会从当前Activity转移到新的Activity中。(在这个案例中,返回消息的目标最开始为B)

坑二:返回无效

但是呢,有一个问题,当我们按返回键时,从D返回到C,再用setResult()向A发送数据,A是只能接收默认的RESULT_CANCLE,这是不能忍的,既然返回键没有将信息传递回去,那么只需要重写onBackPressed()方法,在里面调用setResult(),当然,我是写了BaseActivity,在BaseActivity里面统一处理。代码如下:

1
2
3
4
public void onBackPressed() {
setResult(2);
super.onBackPressed();
}

坑三:启动模式和startActivityForResult()冲突

问题貌似解决了,无论正向还是返回都能传递信息。但是任何事物都是有两面性的,在测试的时候发现还是有个Activity没法通过设置flag来传递信息,反复检查代码,原来是我在那个Activity里面设置了singleTask启动模式,导致了setResult失效,官方文档说明如下:

For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.

这个问题貌似无解,看来singleTask启动模式和startActivityForResult()不能同时使用。但是我需要singleTask的效果啊,有其他的方法实现singleTask的效果么?

答案是肯定的,取消了singleTask之后,只需要将该Activity再设置两个flag:FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_SINGLE_TOP就能得到相同的效果。

坑四:FLAG_ACTIVITY_CLEAR_TOP != singleTask

关于FLAG_ACTIVITY_CLEAR_TOP的解释如下:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.

如果设置,并且这个Activity已经在当前运行的栈中,在此Activity之上的所有Activity会被弹出栈,从而该Activity处于栈顶。

看起来和singleTask启动模式一样?

其实不然,举个例子,假设一个栈中包含这些Activity:A,B,C,D。分别以两种方法从D启动B,会有两种不同的效果:

  • 如果D调用了startActivity()并且以singleTask模式启动B,那么,C和D都将被弹出栈,然后B到了栈顶,因此,目前stack的状况是:A,B。
  • 而如果D以FLAG_ACTIVITY_CLEAR_TOP的方式启动B的话,C和D同样回被弹出,但是B自身会先被销毁一次,然后重新创建一个B。

所以,为了达到和singleTask一样的效果,我们还需要设置另外一个flag:

FLAG_ACTIVITY_SINGLE_TOP:

If set, the activity will not be launched if it is already running at the top of the history stack.

当这个Activity位于Activity栈的顶端运行时,不再启动一个新的。和singleTop启动模式效果相同。

现在,总算把坑都填满了。

如果觉得文章对你有帮助,请我喝杯可乐吧