Android 解决 registerForActivityResult 抛异常 Can only use lower 16 bits for requestCode

  1. 背景:
    使用 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> 提供了请求权限的用法。

 

  1. 代码

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'
  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)的,最终导致报错。

  1. 结论:
    至此,导致问题的原因和解决办法均给出。



 



作者:雁过留声_泪落无痕
链接:https://www.jianshu.com/p/877cc904ae68
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值