Task and the BackStack

场景

  • 新闻类App有新内容推送过来时,从Notification打开详情页面,此时按返回键,返回的是新闻列表页(主页面)。
  • 邮件类型的App,从邮件列表进入到详情,并从详情页面进入到更深的子页面,此时返回键是根据用户浏览习惯返回,顶部导航(ActionBar)返回的是邮件列表页。

实现新闻的效果

PendingIntent携带任务栈信息,配置xml,DetailsActivity配置parentActivity;

  • 配置文件
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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name=".MyApplication">
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- DetailsActivity的parentActivity是MainActivity -->
<activity
android:name=".DetailsActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>

<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
</application>
</manifest>
  • Service里开启一个Notification(just demo)

原理是利用TaskStackBuilder来返回PendingIntent,这样在startActivity的时候会构建Activity栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyService extends Service {
//...
@Override
public void onCreate() {
super.onCreate();
//如果直接使用PendingIntent.getActivity,按下返回键只能返回到launcher界面
//PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, new Intent(this, DetailsActivity.class), PendingIntent.FLAG_ONE_SHOT);
// Construct the Intent you want to end up at
Intent detailActivity = new Intent(this, DetailsActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(detailActivity);
PendingIntent pendingIntent = stackBuilder
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentText("测试内容").
setContentIntent(pendingIntent).
setAutoCancel(true);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, builder.build());
}
}

结果

  1. Notification打开DetailsActivity的时候,此时MainActivity已经销毁,只剩下Service
    05-17 16:07:42.314 4946-4946/com.asura.justfortest I/TAG: onCreate: DetailsActivity
    按下返回键的时候
    05-17 16:07:46.916 4946-4946/com.asura.justfortest I/TAG: onCreate: MainActivity
    好处是本应用没有开启,这种方式返回时可以带到主页去
  2. 当MainActivity和DetailsActivity都存在的情况下,使用这种方式会将app现有的Task栈取代,即现有的所有Activity会销毁,即使设置launchMode为singleTask也没用
  3. 对于不想销毁现有的任务栈的情况,notification配intent时就不要配置TaskStack,针对DetailsActivity可以覆盖返回键,当Task栈为空的时候,启动带Stack的Intent,如果栈不为空,直接finish掉.

顶部导航或者返回键回到父页面(中间的会被销毁)

覆盖onOptionsItemSelected,onBackPress();其中,调用NavUtils.getParentActivityIntent()方法可以获取到跳转至父Activity的Intent,然后如果父Activity和当前Activity是在同一个Task中的,则直接调用navigateUpTo()方法进行跳转,如果不是在同一个Task中的,则需要借助TaskStackBuilder来创建一个新的Task

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
@Override  
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
goToMain();
return true;
}
//...
}
@Override
public void onBackPressed() {
goToMain();
}
private void goToMain() {
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
//设置CLEAR_TOP如果MainActivity存在,会调用onNewIntent()方法
upIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
NavUtils.navigateUpTo(this, upIntent);
}
}

参考