Android N 7.0 以上系统的文件下载报错闪退解决

收到一个客户报过来的问题,换了新采购的设备之后,下载某个文件,然后下载完成之后就闪退。初步觉得应该是系统版本过高导致的权限问题。

后台查看报错日志
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=com.xjj.cloud.ypjczd (has extras) }

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

Android N 7.0 以上系统的文件下载报错闪退解决_第1张图片
报错日志

原因是使用 DownloadManager.COLUMN_LOCAL_FILENAME 报错,看了下代码,这个已经过时了,源码推荐使用 ContentResolver 来获取文件名。

原来的代码

DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
long downId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downId);
Cursor c = manager.query(query);
String filename = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));

使用 ContentResolver 之后

DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
long downId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downId);
Cursor c = manager.query(query);
String filename = "";
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
    int fileUriIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
    String fileUri = c.getString(fileUriIdx);
    if (fileUri != null) {
        filename = Uri.parse(fileUri).getPath();
    }
} else {
    //过时的方式:DownloadManager.COLUMN_LOCAL_FILENAME
    int fileNameIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
    filename = c.getString(fileNameIdx);
}

获取文件名可以获取了,打开文件又报错。抛出异常
android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com./files/1529633205126858.pdf exposed beyond app through Intent.getData()


Android N 7.0 以上系统的文件下载报错闪退解决_第2张图片
文件打开异常

因为之前直接 Uri.fromFile(apkFile) 构建出一个 Uri ,而这个 Uri 在 7.0 以上系统是不能用的,改使用 FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
原先打开文件的代码:

String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory("android.intent.category.DEFAULT");
File file = new File(fileName);
if (file.exists()) {
    intent.setDataAndType(Uri.fromFile(file), getMIMEType(extName));
    context.startActivity(intent);
} else {
    Toast.makeText(context, "文件不存在", Toast.LENGTH_LONG).show();
}

改动之后

String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory("android.intent.category.DEFAULT");
File file = new File(fileName);
if (file.exists()) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri contentUri = FileProvider.getUriForFile(context, 
            context.getApplicationContext().getPackageName() + ".fileprovider", file);
        intent.setDataAndType(contentUri, getMIMEType(extName));
        context.startActivity(intent);
    } else {
        intent.setDataAndType(Uri.fromFile(file), getMIMEType(extName));
        context.startActivity(intent);
    }
} else {
    Toast.makeText(context, "文件不存在", Toast.LENGTH_LONG).show();
}

使用 FileProvider 要同时在配置文件中定义 FileProvider ,在application节点底下,与activity节点同级。添加如下代码


    

还要在 res 目录底下创建一个新的 provider_paths.xml 文件。因为我在 AndroidMenifest 文件中定义的目录是 xml/provider_paths.xml ,所以创建的 provider_paths.xml 文件放在 res/xml 目录底下。



    
        
    

这样子就可以在 Android N 7.0 以上系统正常下载文件以及打开文件了。

你可能感兴趣的:(Android N 7.0 以上系统的文件下载报错闪退解决)