DownloadManager COLUMN_LOCAL_FILENAME 已弃用

14

在 Android N 上,我遇到了一个异常。根据文档,这是已知问题,文档建议我使用 ContentResolver.openFileDescriptor()。

https://developer.android.com/reference/android/app/DownloadManager.html#COLUMN_LOCAL_FILENAME

我不确定如何使用它。这里的 ContentResolver 对象在哪里可以用于获取文件名?我从未使用过它。因此,任何帮助都将不胜感激。

08-04 11:20:59.765 7010 7290 W System.err: java.lang.SecurityException: COLUMN_LOCAL_FILENAME 已弃用;请改用 ContentResolver.openFileDescriptor()

08-04 11:20:59.765 7010 7290 W System.err: at android.app.DownloadManager$CursorTranslator.getString(DownloadManager.java:1499)

以下是我的代码。

    DownloadManager.Query query = new DownloadManager.Query();
    query.setFilterById(id);
    Cursor cursor = downloadManager.query(query);

    final String downloadFilePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
    cursor.close();
我尝试了downlaodManager.getFileUri,但不是我要找的东西。感谢任何帮助。
谢谢。

我假设 COLUMN_LOCAL_URI 会提供给你一个 Uri,以便在 ContentResolver 上调用 openFileDescriptor()openInputStream() 来读取已下载的内容。你可以通过在任何 Context(例如某个 Service)上调用 getContentResolver() 来获取 ContentResolver - CommonsWare
我现在想要获取文件名而不是文件内容。ParcelableFileDescriptor不能给我文件名。 - techtinkerer
我怀疑你能够可靠地获取到的最接近文件名的东西是从COLUMN_LOCAL_URI获取的Uri上的getLastPathSegment() - CommonsWare
我的回答如下,请让我知道您的想法。 - techtinkerer
7个回答

11

以下方法现在对我可行:

    String downloadFilePath = null;
    String downloadFileLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
    if (downloadFileLocalUri != null) {
        File mFile = new File(Uri.parse(downloadFileLocalUri).getPath());
        downloadFilePath = mFile.getAbsolutePath();
    }

我不会假设 COLUMN_LOCAL_URI 总是会给你一个 file:Uri。 你的代码就是这样假设的。 - CommonsWare
我想获取完整的文件路径,例如:/storage/emulated/0/Android/data/com.my.app/files/Download/test.file.download,而file.getAbsolutePath正在返回它。 - techtinkerer
DownloadManager 没有提供该路径的要求。 - CommonsWare
下载管理器返回文件路径:file:///storage/emulated/0/Android/data/com.my.app/files/Download/test.file.download。 - techtinkerer
下载管理器返回 file:///storage/emulated/0/Android/data/com.my.app/files/Download/test.file.down‌​load -- 适用于您的一个设备,适用于您的一个 Android 操作系统版本。不能保证在所有 Android 设备和所有 Android 操作系统版本(过去、现在和未来)中都具有此行为。“我应该调用 Uri.getPath() 吗?”-- 如果这是您自己的下载请求,则应自己保存路径,以便可以在自己的数据库/SharedPreferences/任何其他地方查找它。这样,您就不会依赖于 DownloadManager 的实现细节。 - CommonsWare
显示剩余5条评论

11

我通过使用DownloadManager.COLUMN_LOCAL_URI解决了这个问题,而不是使用DownloadManager.COLUMN_LOCAL_FILENAME

DownloadManager.COLUMN_LOCAL_URI返回包括"file://"在内的文件路径,因此您需要通过使用downloadFilePath = downloadFilePath.replace("file://","");来排除它。

以下是此问题的一行解决方案:

String downloadFilePath = (c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))).replace("file://","");

请查看完整的DownloadManager代码:

DownloadFinishedReceiver.java

public class DownloadFinishedReceiver extends BroadcastReceiver {

    @Override
        public void onReceive(final Context context, Intent intent) {
            String action = intent.getAction();
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action) && intent.getExtras()!=null) {
                Bundle extras = intent.getExtras();
                DownloadManager.Query q = new DownloadManager.Query();
                long downloadId = extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
                q.setFilterById(downloadId);
                Cursor c = ((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).query(q);
                if (c.moveToFirst()) {
                    int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
                    if (status == DownloadManager.STATUS_SUCCESSFUL) {

                        String downloadFilePath = (c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))).replace("file://","");
                        String downloadTitle = c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE));
                        c.close();

                        Log.e("DownloadPath", downloadFilePath); // Print DownloadPath in Logcat
                        Log.e("DownloadTitle", downloadTitle); // Print DownloadTitle in Logcat
                    } else if (status == DownloadManager.STATUS_FAILED) {
                        removeTempOnFailure(context, downloadId);
                    }
                }
            }
        }

        // Remove temp/cache data
        private void removeTempOnFailure(Context con, long downloadId) {
                File cacheFileDir = new File(con.getCacheDir().getAbsolutePath());
                for (File f : cacheFileDir.listFiles()) {
                    if (f.getName().contains(String.valueOf(downloadId))) {
                        f.delete();
                        break;
                    }
                }
            }
}

AndroidMenifest.xml文件中注册广播接收器:

        <receiver
            android:name="com.example.receiver.DownloadFinishedReceiver"
            android:exported="true"
            android:process=".downloadFinished">
            <intent-filter>
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>

将以下方法放入您的Activity中并传递适当的参数:

     /**
     * 
     * @param downloadUrl -> Your file download url
     * @param downloadTitle -> Your file title to display in download manager
     * @param fileName -> filename with extension like music.mp3 it will store in download folder
     * @param hide -> true to hide downloadmanager in status bar, false to display it
     * @return -> it will return downloadId
     */
    private long downloadFromUrl(String downloadUrl, String downloadTitle, String fileName, boolean hide) {
        Uri uri = Uri.parse(downloadUrl);
        DownloadManager.Request request = new DownloadManager.Request(uri);
        request.setTitle(downloadTitle);
        if (hide) {
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
            request.setVisibleInDownloadsUi(false);
        } else
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

        DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        return manager != null ? manager.enqueue(request) : 0;
    }

如果您在上述方法中传递了hide=true,那么您需要在AndroidManifest.xml中添加以下权限。

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

4
使用getUriForDownloadedFile来获取已下载文件的Uri。
DownloadManager downloadManager = DownloadManager.class.cast(getSystemService(DOWNLOAD_SERVICE));
Uri uri = downloadManager.getUriForDownloadedFile(id);

这是错误的答案。这是内部URI,例如“content://”。 - Style-7

1

对于那些觉得有用的人

我必须使用 DownloadManager.COLUMN_URI
而不是 DownloadManager.COLUMN_LOCAL_URI


这是每列的结果

  1. c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)) -> content://downloads/my_downloads/46

  2. c.getString(c.getColumnIndex(DownloadManager.COLUMN_URI)) -> http://test-domain.com/files/encrypted/212125/bsd1e-411cd7-e3229fb-fdddsa12a974.pdf

  3. c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)) -> http://test-domain.com/files/encrypted/212125/bsd1e-411cd7-e3229fb-fdddsa12a974.pdf


1
techtinkerer的回答对我也没有用,因为我并不总是得到一个文件URI,正如CommonsWare所指出的那样,但仍然有几种方法可以从内容URI中获取文件。
1)您可以调用getContentResolver()。openInputStream(myContentUri)来获取输入流,然后将其写入外部或内部存储器中创建的文件。
2)您可以调用getContentResolver()。openFileDescriptor(myContentUri,“r”)来打开只读ParcelFileDescriptor。虽然您无法从ParcelFileDescriptor获取绝对文件路径,但许多接受文件或URI的方法也接受ParcelFileDescriptor。
例如,我正在使用DownloadManager下载PDF,然后使用PdfRenderer打开它,其构造函数接受ParcelFileDescriptor:
PdfRenderer renderer;
ParcelFileDescriptor pdfFileDescriptor = getContentResolver().openFileDescriptor(pdfUri, "r");
if (pdfFileDescriptor != null) renderer = new PdfRenderer(pdfFileDescriptor);
pdfFileDescriptor.close();

举个例子,BitmapFactory还有一种方法可以解码ParcelFileDescriptor:

(来自https://developer.android.com/guide/topics/providers/document-provider.html

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
        getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}

因此,根据您想要做什么,ParcelFileDescriptor可能是您所需要的全部。


1
String downloadFileLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));

以上方法可能会在空行列表的情况下导致CursorIndexOutOfBoundsException
游标是一个对象,它位于第一个条目之前。因此,我们应该首先检查是否有任何结果。这是我的示例:
int index = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);

if (cursor.moveToFirst()) {
    String downloadFileLocalUri = cursor.getString(index);
    if (downloadFileLocalUri != null) {
        File mFile = new File(downloadFileLocalUri);

        downloadFileName = mFile.getName();
        downloadFilePath = mFile.getAbsolutePath();
    }
}
cursor.close();

0

您可以在gradle中将targetSdk降低到小于24。实际上,这种方法并不是推荐的方法,但我们也可以通过将目标sdk降低到23或任何小于24的版本来解决此错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接