Android之startActivityForResult问题

近期在写项目时,在使用startActivityForResult时遇到一些问题,虽然以前也有遇到过,由于时间比较久了,就忘记了。这次开始写博客,又恰逢遇到问题重现,就此写下短篇的记录短文。

先就现在比较常用的FragmengActivity的startActivityForResult源码进行解读:

先查看FragmengActivity的startActivityForResult源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Modifies the standard behavior to allow results to be delivered to fragments.
* This imposes a restriction that requestCode be <= 0xffff.
*/
@Override
public void startActivityForResult(Intent intent, int requestCode) {
// If this was started from a Fragment we've already checked the upper 16 bits were not in
// use, and then repurposed them for the Fragment's index.
if (!mStartedActivityFromFragment) {
if (requestCode != -1) {
checkForValidRequestCode(requestCode);
}
}
super.startActivityForResult(intent, requestCode);
}

通过mStartedActivityFromFragment判断我们的startActivityForResult方法是否从Fragment发起的。如果不是,则需要对requestCode进行检验,至于这里为何要对requestCode检验,我们后面再解释。我们跟着方法进入FragmengtActivity中的父类方法中

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
31
32
33
34
35
36
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}

cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}

具体这里就不往下分析了,继续深入涉及ActivityManager以及ActivityThread的流程。有兴趣可自行查看。

这里说下我们经常遇到的一些问题,在一些APP上,我们需要用到startActivityForResult,来获取某个界面的数据。

现在Android大部分Activity开发基本都是使用FragmentActivity,内部嵌套多个Fragment。这时使用会遇到一些问题,如:

1、 在当前Fragment的onActivityResult的方法未调用,导致无法完成数据获取。

2、 在当前Activity中获取到的requestCode与发起的requestCode不一致。

等。

说下问题1:

当我们在Activity中或者Fragment中通过调用getActivity().startActivityForResult启动下一个activity时,当在activity setResult并调用finish时,回到调用的Activity我们去看下FragmentActivity的源码

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
31
32
33
34
/**
* Dispatch incoming result to the correct fragment.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mFragments.noteStateNotSaved();
int requestIndex = requestCode>>16;
if (requestIndex != 0) {
requestIndex--;

String who = mPendingFragmentActivityResults.get(requestIndex);
mPendingFragmentActivityResults.remove(requestIndex);
if (who == null) {
Log.w(TAG, "Activity result delivered for unknown Fragment.");
return;
}
Fragment targetFragment = mFragments.findFragmentByWho(who);
if (targetFragment == null) {
Log.w(TAG, "Activity result no fragment exists for who: " + who);
} else {
targetFragment.onActivityResult(requestCode & 0xffff, resultCode, data);
}
return;
}

ActivityCompat.PermissionCompatDelegate delegate =
ActivityCompat.getPermissionCompatDelegate();
if (delegate != null && delegate.onActivityResult(this, requestCode, resultCode, data)) {
// Delegate has handled the activity result
return;
}

super.onActivityResult(requestCode, resultCode, data);
}

在此方法中可以看到进入方法后,会对requestCode进行处理,使用过的都知道requestCode的值是有限制的。

此处对requestCode进行右移操作,最终获取的requestCode在正常范围内都是趋于零,导致不会进入条件内,也不会进入Fragment的onActivityResult,若此时需要对fragment的进行回调,则需要自己在代码自行添加。

问题2,进过排查,发现在通过调用fragment的startActivityForResult,在FragmentActivity处获取到的requestCode与发出的requestCode不一致,并且有点随机数,不过该随机数都很大。具体还没详细查阅源码。而且会进入Fragment的onActivityResult,并不会出现问题1的情况。