解决 singleTask onActivityResult() 无效的问题

在 Android 4.X 系统上,如果你将一个 Activity A 的 launchMode 设置为 singleTask 或 singleInstance ,那么当你在 A 中调用 startActivityForResult() 的时候,是不会像你想象的那样,在 onActivityResult() 中获得你想要的返回结果的,就像官方文档说的那样:

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.

我们可以使用一个中介 Activity B 来解决这个问题,思路如下:

  1. 在 A 中通过 startActivity() 调用 B ,向 B 中传递你所需的 Intent I ;

  2. 在 B 中通过 startActivityForResult() 调用 I ;

  3. 在 B 中通过 onActivityResult() 获取 I 的返回结果,通过 Otto 向 A 发送结果,最后再 finish B 。

当然作为中介,我们需要对 B 进行一些配置。下面是一段简单的实例代码。

首先我们可以构造一个简单的 Agent 类,用于在 A 和 B 之间传递 Intent :

public class Agent implements Parcelable {
private Intent intent;
private int requestCode;

// 用于判断是否需要在 AgentActivity 中使用 Intent.setComponentName() ;
// 通常我们使用 Intent ,
// 都是类似 Intent intent = new Intent(Context, Class) 这样使用,
// 所以对于传递给 B 的 Intent ,需要修改对应的 Context ;
// 对于调用系统服务,比如相机,可设置为 false ;
// 对于 App 内部的 Intent ,则设置为 true ;
// 但通常来说如果只是调用 App 内部的 Intent ,
// 其实也没有必要使用中介,直接使用 Otto 就好了。
private boolean cls;

public Agent(Intent intent, int requestCode, boolean cls) {
this.intent = intent;
this.requestCode = requestCode;
this.cls = cls;
}

public Intent getIntent() {
return intent;
}

public int getRequestCode() {
return requestCode;
}

public boolean isCls() {
return cls;
}

// Parcelable 接口的实现...
}

接着我们创建 AgentActivity ,即上文提到的中介 Activity B :

public class AgentActivity extends Activity {
public static final String EXTRA_AGENT = "extra_agent";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (getIntent() == null) {
finish();
return;
}

Agent agent = getIntent().getParcelableExtra(EXTRA_AGENT);
if (agent == null || agent.getIntent() == null) {
finish();
return;
}

// 判断是否需要替换从 A 传递进来的 Intent 的 Context 。
Intent intent = agent.getIntent();
if (agent.isCls()) {
intent.setComponent(
new ComponentName(this, intent.getComponent().getClassName())
);
}

// 通过 B 调用 A 中构造的 Intent 。
startActivityForResult(intent, agent.getRequestCode());
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

// BusProvider 是 Otto 单例的实现, AgentEvent 是自定义的 Otto 事件。
BusProvider.getInstance().post(
new AgentEvent(requestCode, resultCode, data)
);

finish();
}
}

接下来是 AgentEvent 的实现:

public class AgentEvent {
private int requestCode;
private int resultCode;
private Intent data;

public AgentEvent(int requestCode, int resultCode, Intent data) {
this.requestCode = requestCode;
this.resultCode = resultCode;
this.data = data;
}

public int getRequestCode() {
return requestCode;
}

public int getResultCode() {
return resultCode;
}

public Intent getData() {
return data;
}
}

最后我们再在 AndroidManifest.xml 里面添加 AgentActivity 的声明:

<!-- 将 launchMode 设置为 standard ,同时将 theme 设置为不可见。-->
<activity android:name="YOUR_PACKAGE.AgentActivity"
android:launchMode="standard"
android:theme="@android:style/Theme.NoDisplay">
</activity>

现在所有的准备工作都做完了,你只需要在 A 中这样使用就 OK :

public class A extend Activity {

// 为了保证在整个生命周期内能接收到 Otto 的事件,
// 选择在 onCreate() 和 onDestroy() 中注册与注销订阅。
@Override
public void onCreate() {
...

BusProvider.getInstance().register(this);
}

@Override
public void onDestroy() {
...

BusProvider.getInstance().unregister(this);
}

// 通过 AgentActivity 调用系统相机的示例。
private void startAgentToCamera() {
Intent toCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

Agent agent = new Agent(toCamera, YOUR_REQUEST_CODE, false);
Intent toAgent = new Intent(getActivity(), AgentActivity.class);
toAgent.putExtra(AgentActivity.EXTRA_AGENT, agent);
startActivity(toAgent);
}

// 响应来自 AgentActivity 的事件。
@Subscribe
public void onAgentEvent(AgentActivity.AgentEvent event) {
int requestCode = event.getRequestCode();
int resultCode = event.getResultCode();
Intent data = event.getData();

// 处理你接收到的事件...
}
}

当然从上面的例子我们也可以看出,使用一个中介 Activity 的功能不止于此,你还可以用来处理一些只有 Activity 才能接收的 Intent Action 等。当然具体怎么使用,就请发挥你自己的想象力啦~

返回