使用 DownloadManager 时,SD 在 phone 上出现 IllegalStateException

IllegalStateException on phone with SD while using DownloadManager

我在 Play 商店收到关于我的应用出现 ANR 的报告。

这是堆栈跟踪:

java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)

Caused by: java.lang.IllegalStateException: Unable to create directory: /storage/sdcard0/storage/external_SD/Video
at android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.java:496)
at com.pipodi.italiansubsmobileclient.connections.ConnectionForSubtitle.doInBackground(ConnectionForSubtitle.java:61)
at com.pipodi.italiansubsmobileclient.connections.ConnectionForSubtitle.doInBackground(ConnectionForSubtitle.java:21)
at android.os.AsyncTask.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
... 5 more

代码如下:

SharedPreferences preferences = MainActivity.context.getSharedPreferences(Constants.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
    String path = preferences.getString("DOWNLOADDIR", null);
    this.directory = "";
    try {
        directory = path.replace(Environment.getExternalStorageDirectory().getCanonicalPath(), "");
    } catch (IOException e) {
        e.printStackTrace();
    }
    File direct = new File(path);
    if (!direct.exists()) {
        direct.mkdirs();
    }
    DownloadManager mgr = (DownloadManager) MainActivity.context
            .getSystemService(Context.DOWNLOAD_SERVICE);
    Uri downloadUri = Uri
            .parse("https://api.italiansubs.net/api/rest/subtitles/download?subtitle_id="
                    + this.id
                    + "&authcode="
                    + somecodehere
                    + "&apikey="
                    + Constants.APIKey);
    DownloadManager.Request request = new DownloadManager.Request(
            downloadUri);
    List<Cookie> loginCookies = LoginVariables.loginCookies;
    request.setAllowedNetworkTypes(
            DownloadManager.Request.NETWORK_WIFI
                    | DownloadManager.Request.NETWORK_MOBILE)
            .setAllowedOverRoaming(false)
            .setTitle("Download sottotitoli")
            .setDescription(fileName)
            .setDestinationInExternalPublicDir(directory,
                    fileName + ".zip");
    request.allowScanningByMediaScanner();
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    String cookieString = "";
    for (Cookie cookie : loginCookies) {
        cookieString += cookie.getName() + "=" + cookie.getValue() + "; path=" + cookie.getPath()
                + "; domain=" + cookie.getDomain() + "; expiry=" + cookie.getExpiryDate() + "; ";
    }
    request.addRequestHeader("Cookie", cookieString);
    mgr.enqueue(request);
    return null;

出现异常的行是这样的:

request.setAllowedNetworkTypes(
            DownloadManager.Request.NETWORK_WIFI
                    | DownloadManager.Request.NETWORK_MOBILE)
            .setAllowedOverRoaming(false)
            .setTitle("Download sottotitoli")
            .setDescription(fileName)
            .setDestinationInExternalPublicDir(directory,
                    fileName + ".zip");

看来,在带有外部 SD 卡的 phone 上,应用会抛出异常。我告诉用户尝试将文件保存在 phone 的内部存储器上,下载成功完成。现在,我无法创建一个用 SD 卡模拟 phone 的 AVD,所以我在这里问你这个问题。

在清单上有这个权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

我是否还需要授予该应用读取外部存储的权限,或者什么?

该设备是 LG Optimus L5 II (vee5nfc) 运行 Android 4.1.

谢谢大家的帮助。

我的直觉告诉我外部存储无法写入。它可能丢失、只读或安装到计算机上。我会检查它是否可以使用从开发者网站获取的以下代码片段写入。

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

我添加了这个:

@Override
protected void onPreExecute() {
    super.onPreExecute();
    if (isExternalStorageWritable()) {
        Toast.makeText(MainActivity.context, "Writable", Toast.LENGTH_LONG).show();
    } else {
        Toast.makeText(MainActivity.context, "Unwritable", Toast.LENGTH_LONG).show();
    }
}

下载前显示"Writable",但随后抛出异常。没有人将他们的 phone 安装在 PC 上。

最后我解决了这个问题。我认为这是一个 OS 版本相关的问题:最低版本是 4.x,但我修复了将其设置为 4.3.1 的错误。