【我的Android进阶之旅】Android 7.0报异常:java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated;
之前开发的一个和第三方合作的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
【我的Android进阶之旅】Android 7.0报异常:java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated;的更多相关文章
- 【我的Android进阶之旅】解决sqlcipher库:java.lang.IllegalStateException: get field slot from row 0 col 0 failed.
一.背景 最近维护公司的大数据SDK,在大数据SDK里面加入了ANR的监控功能,并将ANR的相关信息通过大数据埋点的方式记录到了数据库中,然后大数据上报的时候上报到大数据平台,这样就可以实现ANR性能 ...
- 我的Android进阶之旅------>Android颜色值(#AARRGGBB)透明度百分比和十六进制对应关系以及计算方法
我的Android进阶之旅-->Android颜色值(RGB)所支持的四种常见形式 透明度百分比和十六进制对应关系表格 透明度 十六进制 100% FF 99% FC 98% FA 97% F7 ...
- 我的Android进阶之旅------>Android中查看应用签名信息
一.查看自己的证书签名信息 如上一篇文章<我的Android进阶之旅------>Android中制作和查看自定义的Debug版本Android签名证书>地址:http://blog ...
- 我的Android进阶之旅------>Android利用温度传感器实现带动画效果的电子温度计
要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight ...
- 我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(三)Android客户端功能实现
我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(一)PC服务器端(地址:http://blog.csdn.net/ouyang_pen ...
- 我的Android进阶之旅------> Android为TextView组件中显示的文本添加背景色
通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article ...
- 我的Android进阶之旅------> Android在TextView中显示图片方法
面试题:请说出Android SDK支持哪些方式显示富文本信息(不同颜色.大小.并包含图像的文本信息),并简要说明实现方法. 答案:Android SDK支持如下显示富文本信息的方式. 1.使用Tex ...
- 我的Android进阶之旅------>Android疯狂连连看游戏的实现之实现游戏逻辑(五)
在上一篇<我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)>中提到的两个类: GameConf:负责管理游戏的 ...
- 我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)
正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piec ...
随机推荐
- vsftpd 服务移植出现 500 oops : socket 解决
一开始, 在vsftpd 打印的错误是 500 oops : socket 在 vsftpd 源码里面找到 buildroot-2016.05/output/build/vsftpd-3.0.3/sy ...
- boa 服务的启动
使用 buildroot 添加 boa 服务后,通过命令 boa 不能启动该服务,就研究了一下boa.conf的配置. etc/boa/boa.conf # 更改user 和Group 为root. ...
- 应用DataAdapter对象更新数据库中的数据
using System.Data.SqlClient; namespace WindowsFormsApplication1 { public partial class Form1 : Form ...
- Unix domain socket IPC
UNIX Domain socket 虽然网络socket也可用于同一台主机的进程间通讯(通过lo地址127.0.0.1),但是unix domain socket用于IPC更有效率:不需要经过网络协 ...
- word调整技巧
a4纸如何留白:
- php if语句判定ms查询是否为空
$select_re_id=$cnx->query("select count(*) c from Yid where pid='".$pid."' and gid ...
- 基于SSH框架实际开发时遇到的问题及解决办法
1. 发现通过注解注入bean不起作用(对应的.java文件上没有'S'标记) 需要在pring .xml配置文件中加 <!-- 使用自动注解就必须配置加入自动扫描加载容器的包 --> & ...
- matlab判断图像是彩色图还是灰度图
matlab怎样看图像是彩色还是灰度_莹莹_新浪博客 http://blog.sina.com.cn/s/blog_76088a1f0101diq0.html 解决一: isrgb(A) 如果A是RG ...
- VC++ GetSafeHwnd用法
GetSafeHwnd HWND GetSafeHwnd() const; 当我们想得到一个窗口对象(CWnd的派生对象)指针的句柄(HWND)时,最安全的方法是使用GetSafeHwnd()函数. ...
- Node.js的全局对象和全局变量
http://blog.csdn.net/leftfist/article/details/41877279