- 背景:
使用 registerForActivityResult() 方法抛 java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode 异常,需添加如下依赖才能使用该方法。
implementation "androidx.activity:activity-ktx:1.3.1"
该方法有多种用途,参考 ActivityResultContract 的子类即可知道具体每个子类提供了一种用法,其中 public static final class RequestPermission extends ActivityResultContract<String, Boolean>
提供了请求权限的用法。
- 代码
class MainActivity : AppCompatActivity() {
private lateinit var mLinearLayout: LinearLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mLinearLayout = LinearLayout(this)
mLinearLayout.orientation = LinearLayout.VERTICAL
setContentView(mLinearLayout)
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
// show camera preview
} else {
finish()
}
}.launch(Manifest.permission.CAMERA)
}
}
报错:
implementation 'androidx.appcompat:appcompat:1.2.0'
解决:
implementation 'androidx.appcompat:appcompat:1.3.0-rc01'
or
implementation 'androidx.appcompat:appcompat:1.3.1'
- 原因:
使用 implementation 'androidx.appcompat:appcompat:1.2.0' 时,自动依赖 1.1.2 版本的 fragment,而 1.1.2 的 FragmentActivity 中还有 startActivityForResult() 方法存在(主要是为区分是在 Activity 还是在 Fragment 中调用的 startActivityForResult)。手动改成 implementation 'androidx.fragment:fragment:1.3.0-beta01' 后,FragmentActivity 中就已经取消了 startActivityForResult() 方法了,所以问题得以解决。另外,也可以直接更新 appcompat 的版本,如 implementation 'androidx.appcompat:appcompat:1.3.0-rc01' 即可(因为它自动依赖的是 androidx.fragment:fragment:1.3.2)
1.2.0 版本的 FragmentActivity.java
/**
* Modifies the standard behavior to allow results to be delivered to fragments.
* This imposes a restriction that requestCode be <= 0xffff.
*/
@Override
public void startActivityForResult(@SuppressLint("UnknownNullness") 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);
}
然而使用 registerForActivityResult 时传递的 requestCode 并没有遵循这个规则,所以报错了,具体可以看 ActivityResultRegistry.java 这类。
ActivityResultRegistry#register() 方法:
public final <I, O> ActivityResultLauncher<I> register(
@NonNull final String key,
@NonNull final LifecycleOwner lifecycleOwner,
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {
Lifecycle lifecycle = lifecycleOwner.getLifecycle();
if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
+ "attempting to register while current state is "
+ lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
+ "they are STARTED.");
}
final int requestCode = registerKey(key);
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
public void onStateChanged(
@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_START.equals(event)) {
mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
if (mParsedPendingResults.containsKey(key)) {
@SuppressWarnings("unchecked")
final O parsedPendingResult = (O) mParsedPendingResults.get(key);
mParsedPendingResults.remove(key);
callback.onActivityResult(parsedPendingResult);
}
final ActivityResult pendingResult = mPendingResults.getParcelable(key);
if (pendingResult != null) {
mPendingResults.remove(key);
callback.onActivityResult(contract.parseResult(
pendingResult.getResultCode(),
pendingResult.getData()));
}
} else if (Lifecycle.Event.ON_STOP.equals(event)) {
mKeyToCallback.remove(key);
} else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
unregister(key);
}
}
};
lifecycleContainer.addObserver(observer);
mKeyToLifecycleContainers.put(key, lifecycleContainer);
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
mLaunchedKeys.add(key);
Integer innerCode = mKeyToRc.get(key);
onLaunch((innerCode != null) ? innerCode : requestCode, contract, input, options);
}
@Override
public void unregister() {
ActivityResultRegistry.this.unregister(key);
}
@NonNull
@Override
public ActivityResultContract<I, ?> getContract() {
return contract;
}
};
}
可以看到 requestCode 是通过 registerKey() 方法生成的。
private int registerKey(String key) {
Integer existing = mKeyToRc.get(key);
if (existing != null) {
return existing;
}
int rc = generateRandomNumber();
bindRcKey(rc, key);
return rc;
}
/**
* Generate a random number between the initial value (00010000) inclusive, and the max
* integer value. If that number is already an existing request code, generate another until
* we find one that is new.
*
* @return the number
*/
private int generateRandomNumber() {
int number = mRandom.nextInt((Integer.MAX_VALUE - INITIAL_REQUEST_CODE_VALUE) + 1)
+ INITIAL_REQUEST_CODE_VALUE;
while (mRcToKey.containsKey(number)) {
number = mRandom.nextInt((Integer.MAX_VALUE - INITIAL_REQUEST_CODE_VALUE) + 1)
+ INITIAL_REQUEST_CODE_VALUE;
}
return number;
}
可以看到,最终是通过 generateRandomNumber() 方法来生成 requestCode,从代码及注释可以看出,生成的整数是完全可能大于 0xffff (即 65535)的,最终导致报错。
- 结论:
至此,导致问题的原因和解决办法均给出。
作者:雁过留声_泪落无痕
链接:https://www.jianshu.com/p/877cc904ae68
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。