Table of contents

Crash due to file storage on Android 10 and above

React Native Dec 26, 2020 Viewed 1.5K Comments 0

Issue

In React Native, I use the following code to download and install the newer apk.

import RNFetchBlob from 'rn-fetch-blob';

let downloadPath = RNFetchBlob.fs.dirs.DownloadDir + "/test.apk";
let config = {
    addAndroidDownloads : {
        useDownloadManager : true,
        title : "test.apk",
        description : 'Downloading apk...',
        mime : 'application/vnd.android.package-archive',
        mediaScannable : true,
        notification : true,
        path: downloadPath,
    }
}
RNFetchBlob.config(config).fetch('GET', "http://localhost/test.apk").then((resp) => {
    RNFetchBlob.android.actionViewIntent(downloadPath, 'application/vnd.android.package-archive')
});

It is no problem when running below android 10. But in android 10 and above, it will report an exception and crash.

18516-18761/com.example E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.example, PID: 18516
    java.lang.ClassCastException: okhttp3.internal.http.RealResponseBody cannot be cast to com.RNFetchBlob.Response.RNFetchBlobFileResp
        at com.RNFetchBlob.RNFetchBlobReq.done(RNFetchBlobReq.java:594)
        at com.RNFetchBlob.RNFetchBlobReq.access$100(RNFetchBlobReq.java:72)
        at com.RNFetchBlob.RNFetchBlobReq$3.onResponse(RNFetchBlobReq.java:493)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)

Solution

Refer to the official document Android storage use cases and best practices. Android 10 introduced a new storage paradigm for apps called scoped storage. Scoped storage changes the way apps store and access files on a device's external storage.

Before your app is fully compatible with scoped storage, you can temporarily opt out by using one of the following methods:
  • Target Android 9 (API level 28) or lower.
  • If you target Android 10 (API level 29) or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file:
<manifest ... >

  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>
Updated Dec 26, 2020