Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

New result API error : Can only use lower 16 bits for requestCode

Today I switched to the new ResultAPI and I faced this error:

java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode
    at androidx.fragment.app.FragmentActivity.checkForValidRequestCode(FragmentActivity.java:715)
    at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:673)
    at androidx.core.app.ActivityCompat.startActivityForResult(ActivityCompat.java:234)
    at androidx.activity.ComponentActivity$2.onLaunch(ComponentActivity.java:207)
    at androidx.activity.result.ActivityResultRegistry$3.launch(ActivityResultRegistry.java:147)
    at androidx.activity.result.ActivityResultLauncher.launch(ActivityResultLauncher.java:42)
    at .MainActivity.getVideo(MainActivity.kt:61)
    at .MainActivity.access$getVideo(MainActivity.kt:18)
    at .MainActivity$onCreate$2.onClick(MainActivity.kt:42)
    at android.view.View.performClick(View.java:5232)
    at android.view.View$PerformClick.run(View.java:21289)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:168)
    at android.app.ActivityThread.main(ActivityThread.java:5885)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)

When executing these lines:

private val takeFile = registerForActivityResult(GetContent()) {
    Log.e("MainActivity", "fileName: $it")
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    btn_get_video.setOnClickListener {
        getVideo()
    }

}


private fun getVideo() {
    takeFile.launch("video/*")
}

There is no way to declare REQUEST_CODE manually with this new result API.

NOTICE:
It sometimes works!

[UPDATE]
I registered multiple contracts (TakeVideo & GetContent) and I found that the order of registration is important (the first one works like a charm but the others will crash). maybe we have to register just a single contract for each activity?!

[UPDATE #2]
The problem has been fixed in the latest version of activity (1.2.7-alpha07). now it's warning you about adding both activity and fragment related dependencies.

like image 313
Kapta Avatar asked Jul 07 '20 09:07

Kapta


2 Answers

Add or update to this dependency:

implementation 'androidx.fragment:fragment:1.3.0'

When using ActivityResult APIs, use this fragment dependency to ensure that FragmentActivity is compatible.

like image 189
Saurabh Thorat Avatar answered Nov 11 '22 23:11

Saurabh Thorat


I think something is wrong with the new API. In ActivityResultRegistry you can see :

 /**
 * 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;
}

In debugged mode i saw "1388473134" sent for request code. Now lets see what is happening. In this case we are using ActivityResultRegistry provided by the framework. This generate a request code hold by the framework and generated by generateRandomNumber from ActivityResultRegistry.

See in ComponentActivity class of google framework androidx.activity.

private ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {

    @Override
    public <I, O> void onLaunch(
            final int requestCode,
            @NonNull ActivityResultContract<I, O> contract,
            I input,
            @Nullable ActivityOptionsCompat options) {

            ...
            
        } else {
            // startActivityForResult path
            ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
        }
}
        

This will call checkForValidRequestCode from FragmentActivity and throw an exception (because the request code generate is too "hight").

@Override
    public void startActivityForResult(@SuppressLint("UnknownNullness") Intent intent,
            int requestCode, @Nullable Bundle options) {
        // 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, options);
    }

To remove the issue you have to update the source of checkForValidRequestCode provided in androidx.fragment.app in your build gradele with a fresh version of this method.

In your gradle file

dependencies {
    def fragment_version = "1.3.0-beta02"

    // Java language implementation
    implementation "androidx.fragment:fragment:$fragment_version"
    // Kotlin
    implementation "androidx.fragment:fragment-ktx:$fragment_version"
    // Testing Fragments in Isolation
    debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
}
like image 36
Zhar Avatar answered Nov 11 '22 23:11

Zhar