Android 7.0 FileProvider的使用

2023-11-27 23:40

本文主要是介绍Android 7.0 FileProvider的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android 7.0 FileProvider的使用

标签: fileproviderandroidContentProvider应用
6278人阅读 评论(1) 收藏 举报
这里转载下, 于连林520wcf   发布的《下载安装APK(兼容Android7.0)》的文章。涉及FileProvider的使用,在此记录参考。


我们使用手机的时候经常会看到应用程序提示升级,大部分应用内部都需要实现升级提醒和应用程序文件(APK文件)下载。
一般写法都差不多,比如在启动app的时候,通过api接口获得服务器最新的版本号,然后和本地的版本号比较,来判断是否需要弹出提示框下载,当然也可以通过推送的自定义消息来实现。
我们这里主要讨论的是应用程序下载,并在通知栏提醒下载完成。实现过程大致分为三步:

创建一个service
在service启动的时候创建一个广播接受者,用于接受下载完成的广播
当BroadcastReceiver接受到下载完成的广播时,开始执行安装。

主要通过系统提供的DownloadManager进行下载,DownloadManager下载完成会发送广播,具体使用看下面完整的代码。如果详细了解可以参考Android系统下载管理DownloadManager功能介绍及使用示例点击打开链接下面创建新的文件DownloadService.Java

[java] view plain copy
  1. public class DownLoadService extends Service {  
  2.     /**广播接受者*/  
  3.     private BroadcastReceiver receiver;  
  4.     /**系统下载管理器*/  
  5.     private DownloadManager dm;  
  6.     /**系统下载器分配的唯一下载任务id,可以通过这个id查询或者处理下载任务*/  
  7.     private long enqueue;  
  8.     /**TODO下载地址 需要自己修改,这里随便找了一个*/  
  9.     private String downloadUrl="http://dakaapp.troila.com/download/daka.apk?v=3.0";  
  10.   
  11.     @Nullable  
  12.     @Override  
  13.     public IBinder onBind(Intent intent) {  
  14.         return null;  
  15.     }  
  16.   
  17.     @Override  
  18.     public int onStartCommand(Intent intent, int flags, int startId) {  
  19.   
  20.         receiver = new BroadcastReceiver() {  
  21.             @Override  
  22.             public void onReceive(Context context, Intent intent) {  
  23.                 install(context);  
  24.                 //销毁当前的Service  
  25.                 stopSelf();  
  26.             }  
  27.         };  
  28.         registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));  
  29.         //下载需要写SD卡权限, targetSdkVersion>=23 需要动态申请权限  
  30.         RxPermissions.getInstance(this)  
  31.                 // 申请权限  
  32.                 .request(Manifest.permission.WRITE_EXTERNAL_STORAGE)  
  33.                 .subscribe(new Action1<Boolean>() {  
  34.                     @Override  
  35.                     public void call(Boolean granted) {  
  36.                         if(granted){  
  37.                             //请求成功  
  38.                             startDownload(downloadUrl);  
  39.                         }else{  
  40.                             // 请求失败回收当前服务  
  41.                             stopSelf();  
  42.   
  43.                         }  
  44.                     }  
  45.                 });  
  46.         return Service.START_STICKY;  
  47.     }  
  48.   
  49.     /** 
  50.      * 通过隐式意图调用系统安装程序安装APK 
  51.      */  
  52.     public static void install(Context context) {  
  53.         Intent intent = new Intent(Intent.ACTION_VIEW);  
  54.         // 由于没有在Activity环境下启动Activity,设置下面的标签  
  55.         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  56.         intent.setDataAndType(Uri.fromFile(  
  57.                 new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "myApp.apk")),  
  58.                 "application/vnd.android.package-archive");  
  59.         context.startActivity(intent);  
  60.     }  
  61.   
  62.     @Override  
  63.     public void onDestroy() {  
  64.         //服务销毁的时候 反注册广播  
  65.         unregisterReceiver(receiver);  
  66.         super.onDestroy();  
  67.     }  
  68.   
  69.     private void startDownload(String downUrl) {  
  70.         //获得系统下载器  
  71.         dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);  
  72.         //设置下载地址  
  73.         DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downUrl));  
  74.         //设置下载文件的类型  
  75.         request.setMimeType("application/vnd.android.package-archive");  
  76.        //设置下载存放的文件夹和文件名字  
  77.  request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "myApp.apk");  
  78.         //设置下载时或者下载完成时,通知栏是否显示  
  79.  request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);  
  80.         request.setTitle("下载新版本");  
  81.         //执行下载,并返回任务唯一id  
  82.         enqueue = dm.enqueue(request);  
  83.     }  
  84. }  

上面代码使用了RxPermissions第三方库动态申请权限,需要在app/build.gradle文件中进行配置

[java] view plain copy
  1. dependencies {  
  2.     //...  
  3.     compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0@aar'  
  4.     compile 'io.reactivex:rxjava:1.1.6' //需要引入RxJava  
  5. }  

记得要配置服务

[java] view plain copy
  1. <application  
  2.   ...>  
  3.     ...  
  4.     <service android:name=".DownLoadService"/>  
  5. </application>  


最后在MainActivity中添加按钮,执行操作。

当下载的时候,会有通知栏进度条提示。下载完成会提示安装。不过当前程序如果在Android7.0上就会报错。下面是报错的日志:

[java] view plain copy
  1. Caused by: android.os.FileUriExposedException:   
  2. file:///storage/emulated/0/Download/myApp.apk exposed beyond app through Intent.getData()  

这是由于Android7.0执行了“StrictMode API 政策禁”的原因,不过小伙伴们不用担心,可以用FileProvider来解决这一问题,
现在我们就来一步一步的解决这个问题。
android 7.0错误原因
随着Android版本越来越高,Android对隐私的保护力度也越来越大。
比如:Android6.0引入的动态权限控制(Runtime Permissions),Android7.0又引入“私有目录被限制访问”,“StrictMode API 政策”。
这些更改在为用户带来更加安全的操作系统的同时也为开发者带来了一些新的任务。如何让你的APP能够适应这些改变而不是crash,是摆在每一位Android开发者身上的责任。
“私有目录被限制访问“ 是指在Android7.0中为了提高私有文件的安全性,面向 Android N 或更高版本的应用私有目录将被限制访问。这点类似iOS的沙盒机制。
" StrictMode API 政策" 是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,应用失败,并出现 FileUriExposedException 异常。
上面用到的代码中的Uri.fromFile  其实就是生成一个file://URL。

[java] view plain copy
  1. //...  
  2. intent.setDataAndType(Uri.fromFile(  
  3.                 new File(Environment.getExternalStoragePublicDirectory(  
  4.                   Environment.DIRECTORY_DOWNLOADS),   
  5.                          "myApp.apk")),  
  6.                 "application/vnd.android.package-archive");  
  7.   
  8. //....  


一旦我们通过这种办法打开其它程序(这里打开系统包安装器)就认为file:// URI类型的 Intent 离开你的应用。这样程序就会发生异常。
接下来就用FileProvider来解决这一问题。


使用FileProvider
使用FileProvider的大致步骤如下:
第一步:在AndroidManifest.xml清单文件中注册provider,因为provider也是Android四大组件之一,可以简单把它理解为向外提供数据的组件,这种组件在实际开发中用的频率并不高,四大组件都可以在清单文件中进行配置。

[java] view plain copy
  1. <application  
  2.    ...>  
  3.     <provider  
  4.         android:name="android.support.v4.content.FileProvider"  
  5.         android:authorities="com.yll520wcf.test.fileprovider"  
  6.         android:grantUriPermissions="true"  
  7.         android:exported="false">  
  8.         <!--元数据-->  
  9.         <meta-data  
  10.             android:name="android.support.FILE_PROVIDER_PATHS"  
  11.             android:resource="@xml/file_paths" />  
  12.     </provider>  
  13. </application>  


第二步:指定共享的目录上面配置文件中 android:resource="@xml/file_paths"  指的是当前组件引用 res/xml/file_paths.xml 这个文件。
我们需要在资源(res)目录下创建一个xml目录,然后创建一个名为“file_paths”(名字可以随便起,只要和在manifest注册的provider所引用的resource保持一致即可)的资源文件,

<files-path/>代表的根目录: Context.getFilesDir()
<external-path/>代表的根目录: Environment.getExternalStorageDirectory()
<cache-path/>代表的根目录: getCacheDir()

上述代码中path="",是有特殊意义的,它代码根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了。
如果你将path设为path="pictures",那么它代表着根目录下的pictures目录(eg:/storage/emulated/0/pictures),如果你向其它应用分享pictures目录范围之外的文件是不行的。
第三步:使用FileProvider上述准备工作做完之后,现在我们就可以使用FileProvider了。我们需要将上述安装APK代码修改为如下

[java] view plain copy
  1. public static void install(Context context) {  
  2.     File file= new File(  
  3.             Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)  
  4.             , "myApp.apk");  
  5.     //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件  
  6.     Uri apkUri =  
  7.             FileProvider.getUriForFile(context, "com.com.yll520wcf.test.fileprovider", file);  
  8.   
  9.     Intent intent = new Intent(Intent.ACTION_VIEW);  
  10.     // 由于没有在Activity环境下启动Activity,设置下面的标签  
  11.     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  12.     //添加这一句表示对目标应用临时授权该Uri所代表的文件  
  13.     intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  14.     intent.setDataAndType(apkUri, "application/vnd.android.package-archive");  
  15.     context.startActivity(intent);  
  16. }  


上述代码中主要有两处改变:

将之前Uri改成了有FileProvider创建一个content类型的Uri。
添加了intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);来对目标应用临时授权该Uri所代表的文件。

上述代码通过FileProvider的Uri getUriForFile (Context context, String authority, File file)静态方法来获取Uri该方法中authority参数就是清单文件中注册provider时填写的authority
android:authorities="com.yll520wcf.test.fileprovider"
按照上面步骤修改就可以兼容Android7.0了。
后期修改,之前没有考虑7.0以下的版本
但是如果此程序在Android7.0以下运行又会报错了,我们需要通过版本判断,当Android7.0及以上需要调用上面的代码,Android7.0以下需要调用7.0以下的代码。这样就OK了。修改install() 方法代码。

[java] view plain copy
  1. /** 
  2.      * 通过隐式意图调用系统安装程序安装APK 
  3.      */  
  4.     public static void install(Context context) {  
  5.         File file = new File(  
  6.                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)  
  7.                 , "myApp.apk");  
  8.         Intent intent = new Intent(Intent.ACTION_VIEW);  
  9.         // 由于没有在Activity环境下启动Activity,设置下面的标签  
  10.         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  11.         if(Build.VERSION.SDK_INT>=24) { //判读版本是否在7.0以上  
  12.             //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件  
  13.             Uri apkUri =  
  14.                     FileProvider.getUriForFile(context, "com.a520wcf.chapter11.fileprovider", file);  
  15.             //添加这一句表示对目标应用临时授权该Uri所代表的文件  
  16.             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  17.             intent.setDataAndType(apkUri, "application/vnd.android.package-archive");  
  18.         }else{  
  19.             intent.setDataAndType(Uri.fromFile(file),  
  20.                     "application/vnd.android.package-archive");  
  21.         }  
  22.         context.startActivity(intent);  
  23.     }  






5
1
相关文章推荐
  • Android7.0之FileProvider
  • FileProvider的使用
  • 使用FileProvider共享文件
  • Android 7.0 安装失败的两个原因
  • Content Provider(二)之 FileProvider 实现应用文件共享
  • FileProvider使用
  • 根据 Android Training课程写的FileProvider小例子
  • FileProvider
  • Android 应用间共享文件(FileProvider)
  • FileProvider使用
猜你在找
【直播】机器学习&数据挖掘7周实训--韦玮
【套餐】系统集成项目管理工程师顺利通关--徐朋
【直播】3小时掌握Docker最佳实战-徐西宁
【套餐】机器学习系列套餐(算法+实战)--唐宇迪
【直播】计算机视觉原理及实战--屈教授
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之矩阵--黄博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之凸优化--马博士
【套餐】Javascript 设计模式实战--曾亮
查看评论
1楼 车凤龙 2017-03-30 13:59发表 [回复]
学习了
发表评论
  • 用 户 名:
  • www5256246
  • 评论内容:
  • 插入代码
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
  • 个人资料

  • 小队长1号
    1
    • 访问:21320次
    • 积分:578
    • 等级:
    • 排名:千里之外
    • 原创:33篇
    • 转载:4篇
    • 译文:0篇
    • 评论:6条
  • 文章分类
  • 设计模式(4)
  • 文章存档
    • 2016年12月(3)
    • 2016年11月(5)
    • 2016年10月(5)
    • 2016年09月(10)
    • 2016年06月(2)
      展开
  • 阅读排行
  • Android 7.0 FileProvider的使用(6265)
  • RXBUS的简单使用(3095)
  • Android如何关闭硬件加速(2334)
  • 寻找android中的设计模式(一)(1282)
  • Activity过度动画应用(828)
  • 基于N源码的AccountManagerService简单认识和账户添加流程分析(663)
  • 基于N源码的Activity的启动过程分析(594)
  • AIDL简单使用(584)
  • 基于N源码的ContentProvider调用流程分析(524)
  • Android电话本数据查询总结(307)
  • 评论排行
  • Android电话本数据查询总结(3)
  • 寻找android中的设计模式(一)(1)
  • 基于N源码的Activity的启动过程分析(1)
  • Android 7.0 FileProvider的使用(1)
  • android中的动画(0)
  • android数据库更新简介(0)
  • 重要的View(0)
  • Broadcast和ContentProvider(0)
  • Service和AIDL(0)
  • 寻找android中的设计模式(三)(0)
  • 推荐文章
    • * CSDN日报20170725——《新的开始,从研究生到入职亚马逊》
    • * 深入剖析基于并发AQS的重入锁(ReetrantLock)及其Condition实现原理
    • * Android版本的"Wannacry"文件加密病毒样本分析(附带锁机)
    • * 工作与生活真的可以平衡吗?
    • * 《Real-Time Rendering 3rd》 提炼总结——高级着色:BRDF及相关技术
    • * 《三体》读后思考-泰勒展开/维度打击/黑暗森林
  • 最新评论
  • Android 7.0 FileProvider的使用

    车凤龙:学习了

  • Android电话本数据查询总结

    zhouyl372:03-22 11:26:07.166 4442 4442 D zyl : at jav...

  • Android电话本数据查询总结

    zhouyl372:03-22 11:26:07.166 4442 4442 D zyl : at and...

  • Android电话本数据查询总结

    zhouyl372:打堆栈可以看到ContactsProvider2是由ActivityThread启动,Activit...

  • 基于N源码的Activity的启动过程分析

    bwv1052:写的不错

  • 寻找android中的设计模式(一)

    yymmbb:下面的图挂了

这篇关于Android 7.0 FileProvider的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/428678

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的