[背景]
当一个业务流程涉及到多个task之间的交互,如一个Task_A中的Activity_1 startActivityForResult() 了一个Activity_2, Activity_2启动另一个Task_B并且期望利用Task_B运行的结果setResult()返回给Activity_1。
TaskRecord如下图所示:
[问题]
Task_B取得result之后,需要
把Task_A带到前台(active)
,这样Activity_1才能响应(onActivityResult)到Activity_2返回(setResult)的结果。
现在的问题就在于,Task_B并不一定能够退出(Activity_3其可能其他程序,如browser等),从而把Task_A带到前台;又或者Task_A和Task_B中又夹杂了其他的Task(你知道,长按home键你可以做得出来),以至于调用Task_B.moveTaskToBack()也不能解决问题。
[解决方案]
如果
你的平台是面向Android3.0(API level 11),那么恭喜你,一个函数就可以搞定:
moveTaskToFront
http://developer.android.com/reference/android/app/ActivityManager.html#moveTaskToFront(int, int)
如果你和我一样苦逼的在用2.2的平台,那么就要稍微麻烦点了,基本上是用
taskAffinity
+FLAG_ACTIVITY_NEW_TASK +
FLAG_ACTIVITY_SINGLE_TOP。
先来看一下sdk怎么说的:
1. taskAffinity
:
SDK说,如果用带FLAG_ACTIVITY_NEW_TASK标志来启动一个activity,并且这个activity与当前运行的另一个task(A)的affinity名字相同,那么就这个activity就会在那个task(A)里启动。
Ok,到这还不行,人家没说会把Task_A带到前台,接着看另一个Flag
2. FLAG_ACTIVITY_NEW_TASK
好了,重点来了:如果Task_A中已经有了Activity_2,这时候如果以FLAG_ACTIVITY_NEW_TASK来start Activity_2,那么这个Task_A就会被
带到前台。
好了,现在Task_A已经可以到前台了,剩下最后一步:Activity_2不能创建两次,这就要求同时使用下面这个flag:
3. FLAG_ACTIVITY_SINGLE_TOP
如果Activity_2在Task的顶端,那么这个Activity_2就不会被再此创建,而是走onNewIntent.
至此,结合上面3个特性,问题得以解决。结合代码,流程如下:
1. 在androdiManifest.xml中添加Activity_2的taskAffinity name与Task_A相同(也就是与Activity_1的taskAffinity名字相同),如android:taskAffinity = "android.task.calendar"。
2. 在Task_B启动前,Activity_1先启动Activity_2并要求返回结果,startActivityForResult().
3. Task_B中的Activity_4取得所需数据后,保存好(intent,或其他方法),然后startActivity(Activity_2),并带上(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP)标志。
4. Task_A被带到前台,并且调用到Activity_2::onNewIntent().
5. Activity_2取得之前保存的信息(从Intent或其他地方),调用setResult(),然后finish().
6. Activity_1中的onActivityResult()响应,得到返回结果。
总结下:
Android 显示 Activity 到前台
Java代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<uses-permission android:name=
"android.permission.REORDER_TASKS"
/>
public
class
SampleActivity
extends
Activity {
private
ActivityManager.RunningAppProcessInfo getRunningAppProcessInfo(String packageName) {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processList = am.getRunningAppProcesses();
for
(ActivityManager.RunningAppProcessInfo p : processList) {
if
(p.processName.equals(packageName)) {
return
p;
}
}
return
null
;
}
private
boolean
isForeground(String packageName) {
ActivityManager.RunningAppProcessInfo processInfo = getRunningAppProcessInfo(packageName);
if
(processInfo !=
null
) {
return
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND == processInfo.importance;
}
return
false
;
}
private
boolean
moveTaskToFront() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(getTaskId(),
0
);
return
isForeground(getPackageName());
}
}
|
描述:getRunningAppProcessInfo 获得当前进程的RunningAppProcessInfo; isForeground 判断是否在前台 moveTaskToFront 把Activity切换到前台
有一个Android应用包含包含一个后台程序,该程序会定期连接服务器来实现自定义信息的推送。但是,当这个应用处于前台的时候,后台程序就没有必要连接服务器了。这样可以节省网络资源,也更省电。
用什么方法知道该应用是否处于前台呢?
网上搜到的方法大多数都是使用下面的代码:
1
2
3
4
5
6
|
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
//获得task列表
List<ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);
Log.d("topActivity", "CURRENT Activity ::"+ taskInfo.get(0).topActivity.getClassName());
ComponentName componentInfo = taskInfo.get(0).topActivity;
componentInfo.getPackageName(); |
但是查阅Android文档后发现,google并不推荐使用这个方法:
This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect.
而且,这个方法还要求设置android.permission.GET_TASKS
权限。
因此,我必须寻找更加合适的方法来做这件事。最终,我找到这个方法getRunningAppProcesses(),它并不需要增加特殊的权限。
下面是范例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * 返回当前的应用是否处于前台显示状态 * @param $packageName * @return */
private boolean isTopActivity(String $packageName)
{
//_context是一个保存的上下文
ActivityManager __am = (ActivityManager) _context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> __list = __am.getRunningAppProcesses();
if(__list.size() == 0) return false;
for(ActivityManager.RunningAppProcessInfo __process:__list)
{
Log.d(getTAG(),Integer.toString(__process.importance));
Log.d(getTAG(),__process.processName);
if(__process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
__process.processName.equals($packageName))
{
return true;
}
}
return false;
} |
技术
Android, JAVA