ActivityResult 的一切
Android 中 Activity 直接通信的方式一般是
- A.startActivityForResult(B)
- B 在 finish()之前调用 setResult()存入结果,关闭 B 页面
- 回到 A 页面之后,在 onActivityResult 中接收结果
这种典型的通信方式相信大家早就习以为常了,但是本文要说几个你可能没有注意到的点
1.这种方式能够稳定跨进程通信
是的,你没有听过,一般跨进程通信都是 AIDL,Service。但是在国内各种乱七八糟的 ROM 厂商系统中,很多都是把跨进程唤起 service,content provider,receiver 给禁止掉了。
为什么这么做?因为国内没有 GooglePlay 之类的唯一应用分发平台,导致市面上各种乱七八糟,各大 APP为了包活,经常 是你唤起我,我唤起你,这种唤起肯定是跨进程的。Rom 厂商肯定不愿意看到自己的系统被搞的乌烟瘴气,所以索性把各种非必要的冷唤起给禁用了。(PS:在华为手机上,冷唤起 Service 就能看到 log 里面的提示,之前小米手机的某个版本开始把唤起支付宝的服务给禁用调了,导致当时一大片用户无法支付,可怕,流氓),后来是小米下发了白名单,此事才作罢。
那支付宝以后会不会每天都胆战心惊呢?除了小米,国内 OV,一大堆国产收集2以后也会不会这么干!!!索性支付宝进行了一次升级(不要问我为什么对这个事情这么关注,我也是做某 BAT 支付的,我们的支付唤起也被禁用了)。支付宝升级的方式就是通过 startActivity 唤起支付(微信很机智,从一开始就是这么干的,不得不佩服微信支付团队)。
那么又有人要问了,会不会 Romc厂商以后把跨进程 startActivity 也禁用呢?
讲了一大堆历史,也告诫我们,用最简单的方式去做,往往是最稳妥的。
2.什么时候调用 setResult
我们可能随手在某个地方写了 setResult,但是有没有想过,什么时候把 result 回调给上一个页面呢?我们调用了就会回调给上一个页面吗?二话不说,扔一坨代码如下:
1
2
3
4
5
6
public final void setResult(int resultCode) {
synchronized (this) {
mResultCode = resultCode;
mResultData = null;
}
}
我们的 setResult 只是把数据保存到 mResultCode,mResultData,所以你只要在 finish 之前,可以任意次调用 setResult()
那么问题来了,到底是什么时候回调给上一个页面呢?在扔一坨代码:
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
private void finish(int finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
if (ActivityManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
} else {
mParent.finishFromChild(this);
}
}
看到了吧,在调用 finish 的时候,会把 resultCode,resultData 通过 AMS回调给唤起页面。
3.onActivityResult 的封装
参考我另外一个文章,封装成链式调用,感受下面这一坨代码github:
1
2
3
4
5
6
7
8
9
10
11
AvoidOnResult(this).startForResult(FetchDataActivity::class.java, object : AvoidOnResult.Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) =
if (resultCode == Activity.RESULT_OK) {
val text = data?.getStringExtra("text")
Toast.makeText(this@MainActivity, "callback -> " + text, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity, "callback canceled", Toast.LENGTH_SHORT).show()
}
})