Crash due to file storage on Android 10 and above
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
totrue
in your app's manifest file:
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>