之前开发的一个和第三方合作的apk,在之前公司的 Android 5.1 系统的手表上运行正常,今天在公司新开发的 Android 7.1系统的手表上运行的时候,使用 DownloadManager 下载之后,查询下载状态的时候,报了异常

java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated; use ContentResolver.openFileDescriptor() instead

异常详细信息如下:

03-17 15:59:43.288 31487-31487/com.netease.xtc.cloudmusic E/CloudMusicDownloadService: DownloadChangeObserver.onChange() throwable = java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated; use ContentResolver.openFileDescriptor() instead
at android.app.DownloadManager$CursorTranslator.getString(DownloadManager.java:1545)
at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService.queryDownloadStatus(CloudMusicDownloadService.java:634)
at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService.access$900(CloudMusicDownloadService.java:55)
at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService$DownloadChangeObserver$3.call(CloudMusicDownloadService.java:590)
at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService$DownloadChangeObserver$3.call(CloudMusicDownloadService.java:587)
at rx.Observable.unsafeSubscribe(Observable.java:8666)
at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:220)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)

跳转到报错的地方,如下图所示:

在运行下面两行代码的时候报错了。

  int fileNameIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
String fileName = c.getString(fileNameIdx);

而且 DownloadManager.COLUMN_LOCAL_FILENAME 已经是被废弃的常量了,点击查看源代码如下所示:

 /**
* Path to the downloaded file on disk.
* <p>
* Note that apps may not have filesystem permissions to directly access
* this path. Instead of trying to open this path directly, apps should use
* {@link ContentResolver#openFileDescriptor(Uri, String)} to gain access.
*
* @deprecated apps should transition to using
* {@link ContentResolver#openFileDescriptor(Uri, String)}
* instead.
*/
@Deprecated
public final static String COLUMN_LOCAL_FILENAME = "local_filename";

Android 在 Android 7.0 或更高版本开发的应用在尝试访问DownloadManager.COLUMN_LOCAL_FILENAME 时会触发java.lang.SecurityException。

Android官方建议我们使用 ContentResolver#openFileDescriptor(Uri, String)来获取文件相关信息。源代码如下所示:

 /**
* Open a raw file descriptor to access data under a URI. This
* is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
* underlying {@link ContentProvider#openFile}
* ContentProvider.openFile()} method, so will <em>not</em> work with
* providers that return sub-sections of files. If at all possible,
* you should use {@link #openAssetFileDescriptor(Uri, String)}. You
* will receive a FileNotFoundException exception if the provider returns a
* sub-section of a file.
*
* <h5>Accepts the following URI schemes:</h5>
* <ul>
* <li>content ({@link #SCHEME_CONTENT})</li>
* <li>file ({@link #SCHEME_FILE})</li>
* </ul>
*
* <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
* on these schemes.
* <p>
* If opening with the exclusive "r" or "w" modes, the returned
* ParcelFileDescriptor could be a pipe or socket pair to enable streaming
* of data. Opening with the "rw" mode implies a file on disk that supports
* seeking. If possible, always use an exclusive mode to give the underlying
* {@link ContentProvider} the most flexibility.
* <p>
* If you are writing a file, and need to communicate an error to the
* provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openFile
* ContentProvider.openFile}.
* @return Returns a new ParcelFileDescriptor pointing to the file. You
* own this descriptor and are responsible for closing it when done.
* @throws FileNotFoundException Throws FileNotFoundException if no
* file exists under the URI or the mode is invalid.
* @see #openAssetFileDescriptor(Uri, String)
*/
public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
@NonNull String mode) throws FileNotFoundException {
return openFileDescriptor(uri, mode, null);
}

查看Android 官方文档,关于Android 7.0 的权限管理更改,如下图所示:

参考链接为:

https://developer.android.google.cn/about/versions/nougat/android-7.0-changes.html#sharing-files

因此为了解决这个异常,我们有以下两个方法解决。

方法一、使用 ContentResolver#openFileDescriptor(Uri, String)来替代访问由 DownloadManager 公开的文件。

方法二、使用DownloadManager.COLUMN_LOCAL_URI查出文件Uri,然后使用Uri new一个File,再获取File的相关信息,如下所示:

int fileUriIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String fileUri = c.getString(fileUriIdx);
String fileName = null;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
if (fileUri != null) {
fileName = Uri.parse(fileUri).getPath();
}
} else {
//Android 7.0以上的方式:请求获取写入权限,这一步报错
//过时的方式:DownloadManager.COLUMN_LOCAL_FILENAME
int fileNameIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
fileName = c.getString(fileNameIdx);
}

运行之后,不会报错,并且能够获取到正常的文件名。

参考链接

1. https://developer.android.google.cn/about/versions/nougat/android-7.0-changes.html#sharing-files

2. http://www.cnblogs.com/dazhao/p/6547811.html

3. http://www.jianshu.com/p/56b9fb319310


作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!

转载请保留原文地址: http://blog.csdn.net/ouyang_peng/article/details/62891782

最新文章

  1. TP-LINK telnet远程 重启路由器
  2. Windows Azure Virtual Machine (29) 修改Azure VM 数据磁盘容量
  3. try throw catch
  4. spark 读取 elasticsearch数据
  5. 【液晶模块系列基础视频】5.2.X-GUI字体驱动2
  6. Centos 安装 p7zip,即Linux下的7z
  7. 1076. Forwards on Weibo (30)
  8. css揭秘之按钮的实现技巧
  9. Web API的CPU占用100%
  10. js收集错误信息,错误上报
  11. 免费搭建wordpress博客有感
  12. 十一招让Ubuntu 16.04用起来更得心应手(转)
  13. Gym 100952B&amp;&amp;2015 HIAST Collegiate Programming Contest B. New Job【模拟】
  14. 从Node到Go的心路之旅
  15. jenkins+svn 打包部署上传服务器并正常运行项目,从安装到使用
  16. Java版统计文件中的每个单词出现次数
  17. 20175310 迭代和JDB
  18. Flume source 支持的type类型
  19. 【BootStrap】Table的基本使用
  20. [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

热门文章

  1. Centos 挂载NTFS格式的USB硬盘
  2. Odoo 8.0 new API 概述
  3. Ubuntu 下添加OpenERP command 快捷启动方式
  4. 框架Iframe的退出,IE 火狐都没问题 到360就不跳转页面 刷新一遍才跳转到登录页
  5. 在堆栈中,push为入栈操作,pop为出栈操作
  6. Service层事务不能回滚的解决方式
  7. 基于SSH框架实际开发时遇到的问题及解决办法
  8. 如何用ChemDraw绘制化学课件
  9. Ubuntu 16.04 LTS 安装libvips出现”Package vips was not found in the pkg-config search path”
  10. AWS系列-EC2默认限制说明