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

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

异常详细信息如下:

  1. 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
  2. at android.app.DownloadManager$CursorTranslator.getString(DownloadManager.java:1545)
  3. at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService.queryDownloadStatus(CloudMusicDownloadService.java:634)
  4. at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService.access$900(CloudMusicDownloadService.java:55)
  5. at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService$DownloadChangeObserver$3.call(CloudMusicDownloadService.java:590)
  6. at com.netease.xtc.cloudmusic.services.download.CloudMusicDownloadService$DownloadChangeObserver$3.call(CloudMusicDownloadService.java:587)
  7. at rx.Observable.unsafeSubscribe(Observable.java:8666)
  8. at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
  9. at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:220)
  10. at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
  11. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
  12. at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  13. at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
  14. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
  15. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
  16. at java.lang.Thread.run(Thread.java:761)

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

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

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

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

  1. /**
  2. * Path to the downloaded file on disk.
  3. * <p>
  4. * Note that apps may not have filesystem permissions to directly access
  5. * this path. Instead of trying to open this path directly, apps should use
  6. * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain access.
  7. *
  8. * @deprecated apps should transition to using
  9. * {@link ContentResolver#openFileDescriptor(Uri, String)}
  10. * instead.
  11. */
  12. @Deprecated
  13. 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)来获取文件相关信息。源代码如下所示:

  1. /**
  2. * Open a raw file descriptor to access data under a URI. This
  3. * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
  4. * underlying {@link ContentProvider#openFile}
  5. * ContentProvider.openFile()} method, so will <em>not</em> work with
  6. * providers that return sub-sections of files. If at all possible,
  7. * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
  8. * will receive a FileNotFoundException exception if the provider returns a
  9. * sub-section of a file.
  10. *
  11. * <h5>Accepts the following URI schemes:</h5>
  12. * <ul>
  13. * <li>content ({@link #SCHEME_CONTENT})</li>
  14. * <li>file ({@link #SCHEME_FILE})</li>
  15. * </ul>
  16. *
  17. * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
  18. * on these schemes.
  19. * <p>
  20. * If opening with the exclusive "r" or "w" modes, the returned
  21. * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
  22. * of data. Opening with the "rw" mode implies a file on disk that supports
  23. * seeking. If possible, always use an exclusive mode to give the underlying
  24. * {@link ContentProvider} the most flexibility.
  25. * <p>
  26. * If you are writing a file, and need to communicate an error to the
  27. * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
  28. *
  29. * @param uri The desired URI to open.
  30. * @param mode The file mode to use, as per {@link ContentProvider#openFile
  31. * ContentProvider.openFile}.
  32. * @return Returns a new ParcelFileDescriptor pointing to the file. You
  33. * own this descriptor and are responsible for closing it when done.
  34. * @throws FileNotFoundException Throws FileNotFoundException if no
  35. * file exists under the URI or the mode is invalid.
  36. * @see #openAssetFileDescriptor(Uri, String)
  37. */
  38. public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
  39. @NonNull String mode) throws FileNotFoundException {
  40. return openFileDescriptor(uri, mode, null);
  41. }

查看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的相关信息,如下所示:

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

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

参考链接

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

【我的Android进阶之旅】Android 7.0报异常:java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated;的更多相关文章

  1. 【我的Android进阶之旅】解决sqlcipher库:java.lang.IllegalStateException: get field slot from row 0 col 0 failed.

    一.背景 最近维护公司的大数据SDK,在大数据SDK里面加入了ANR的监控功能,并将ANR的相关信息通过大数据埋点的方式记录到了数据库中,然后大数据上报的时候上报到大数据平台,这样就可以实现ANR性能 ...

  2. 我的Android进阶之旅------>Android颜色值(#AARRGGBB)透明度百分比和十六进制对应关系以及计算方法

    我的Android进阶之旅-->Android颜色值(RGB)所支持的四种常见形式 透明度百分比和十六进制对应关系表格 透明度 十六进制 100% FF 99% FC 98% FA 97% F7 ...

  3. 我的Android进阶之旅------>Android中查看应用签名信息

    一.查看自己的证书签名信息 如上一篇文章<我的Android进阶之旅------>Android中制作和查看自定义的Debug版本Android签名证书>地址:http://blog ...

  4. 我的Android进阶之旅------>Android利用温度传感器实现带动画效果的电子温度计

    要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight ...

  5. 我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(三)Android客户端功能实现

    我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(一)PC服务器端(地址:http://blog.csdn.net/ouyang_pen ...

  6. 我的Android进阶之旅------> Android为TextView组件中显示的文本添加背景色

    通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article ...

  7. 我的Android进阶之旅------> Android在TextView中显示图片方法

    面试题:请说出Android SDK支持哪些方式显示富文本信息(不同颜色.大小.并包含图像的文本信息),并简要说明实现方法. 答案:Android SDK支持如下显示富文本信息的方式. 1.使用Tex ...

  8. 我的Android进阶之旅------>Android疯狂连连看游戏的实现之实现游戏逻辑(五)

    在上一篇<我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)>中提到的两个类: GameConf:负责管理游戏的 ...

  9. 我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)

    正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piec ...

随机推荐

  1. 基于jQuery的时间轴鼠标悬停动画插件

    之前为大家分享了很多jquery插件,这次我们要来分享一款不太常见的jQuery插件,它是一个时间轴,时间轴上的每一个点在鼠标滑过时都可以显示该点的描述信息,并且鼠标滑过时都可以产生一定的动画特效,比 ...

  2. Apache Rewrite 拟静态

    mod_rewrite是Apache的一个非常强大的功能,它可以实现伪静态页面.一些防盗链就是通过该方法做到的. 00x1 启动rewrite引擎 00x2 如何启用apache rewrite? 0 ...

  3. 如何让jquery-easyui的combobox像select那样不可编辑

    http://zhidao.baidu.com/link?url=td61iIn_MBCs1FvT7b-B9Lp9VzlyrcnGmSbkCy1EsSzuod5o47zTmJFRQ-xizxdqv1E ...

  4. python 神经网络实例

    #http://python.jobbole.com/82758/ # import numpy as np # # # # sigmoid function # def nonlin(x, deri ...

  5. UCASE() 函数

    UCASE() 函数 UCASE 函数把字段的值转换为大写. SQL UCASE() 语法 SELECT UCASE(column_name) FROM table_name

  6. python爬虫<urlopen error [Errno 10061] >

    在网上看了十几篇文章,都是说的是IE的代理设置,具体是: Tools->Internet Options->Connections->Lan Settings 将代理服务器的小勾勾去 ...

  7. linux的tcz文件怎么安装?

    .tcz文件是Tiny core linux 应用安装包文件启动Tiny core linux后,可以使用tce-load命令安装软件如: tce-load -i /tmp/bftpd.tcz 转自: ...

  8. WebService的初级学习

    复习准备 1. Schema约束: 1.1   namespace相当于Schema文件的id: 1.2   targetNamespace属性用来指定schema文件的namespace的值; 1. ...

  9. PHP cURL库函数抓取页面内容

    目录 1 为什么要用cURL? 2 启用cURL 3 基本结构 4 检查错误 5 获取信息 6 基于浏览器的重定向 7 用POST方法发送数据 8 文件上传 9 cURL批处理(multi cURL) ...

  10. 去除 \ufeff

    语言:python 编程工具:pycharm 硬件环境:win10 64位 读取文件过程中发现一个问题:已有记事本文件(非空),转码 UTF-8,复制到pycharm中,在开始位置打印结果会出现  \ ...