Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

FileProvider N 7.0 升级 安装APK 选择文件 拍照 临时权限 MD


目录

目录
问题
官方文档的相关描述
配置
声明 FileProvider
配置 resource
系统提供的各种文件路径
使用案例
安装指定路径的apk
拍照并指定保存位置

问题

我们在开发 app 时避免不了需要添加应用内升级功能。当 app 启动时,如果检测到最新版本,将 apk 安装包从服务器下载下来,执行安装。
安装apk的代码一般写法如下,网上随处可以搜到

public static void installApk(Context context, File file) {Intent intent = new Intent(Intent.ACTION_VIEW);Uri data = Uri.fromFile(file); //核心代码intent.setDataAndType(data, "application/vnd.android.package-archive");context.startActivity(intent);
}

然而,当我们在Android7.0手机中执行时,会发现会报如下错误日志:

Caused by: android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/net.csdn.blog.ruancoder/cache/test.apk exposed beyond app through Intent.getData()at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)at android.net.Uri.checkFileUriExposed(Uri.java:2346)at android.content.Intent.prepareToLeaveProcess(Intent.java:8933)at android.content.Intent.prepareToLeaveProcess(Intent.java:8894)at android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)at android.app.Activity.startActivityForResult(Activity.java:4224)at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:50)at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:79)at android.app.Activity.startActivityForResult(Activity.java:4183)at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:859)at android.app.Activity.startActivity(Activity.java:4507)at android.app.Activity.startActivity(Activity.java:4475)

官方文档的相关描述

FileUriExposedException官方文档 的一些描述:

  • 当应用程序将文件以【file://】形式的Uri公开到另一个应用程序时抛出的异常
  • 不鼓励这种曝光方式,因为接收的app可能无法访问你所共享的路径。例如,接收app可能未请求运行时权限Manifest.permission.READ_EXTERNAL_STORAGE ,或者平台可能跨用户配置文件边界[user profile boundaries]共享Uri。
  • 相反,应用程序应使用【content://】形式的Uris,以便平台可以扩展接收应用程序的临时权限以访问资源。
  • 仅针对 Build.VERSION_CODES.N 或更高版本的应用程序抛出此操作。 早期SDK版本的app可以以【file://】形式的Uri共享文件,但强烈建议不要这样做。

FileProvider 官方文档 的一些描述:

  • FileProvider是ContentProvider的一个特殊子类,它通过创建 content:// Uri 而不是 file:/// Uri 来促进与应用程序关联的文件的安全共享。
  • content URI 允许您使用临时访问权限来获取读写访问权限。当您创建包含 content URI 的Intent时,为了将 content URI 发送到客户端app,您还可以调用 Intent.setFlags() 来添加权限。只要接收 Activity 的堆栈处于活动状态,客户端应用程序就可以使用这些权限。对于转到Service的Intent,只要Service正在运行,权限就可用。
  • 相比之下,要控制对 file:/// Uri 的访问,您必须修改基础文件的文件系统权限。您提供的权限可供任何app使用,并在您更改之前保持有效。这种访问level从根本上说是不安全的。
  • 通过 content URI 提高文件访问安全性使FileProvider成为Android安全基础架构的关键部分。

配置

不要再去看垃圾官方文档了,也不要去看网上各种垃圾文章了,也不要通过看源码什么的去研究怎么个性化设置了,MLGB,我在几个月的时间内几次尝试理清怎么配置,结果还是发现了各种各样的bug,真的不要再折腾了,这些个性化的东西不重要,只要按照我下面的配置就行了,保证是最简单稳定的。

声明 FileProvider

在清单文件中声明FileProvider:

<manifest><application><providerandroid:name="android.support.v4.content.FileProvider"android:authorities="${applicationId}.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider></application>
</manifest>

其中:

  • android:name 固定写法

    如果要覆盖FileProvider方法的任何默认行为,可扩展FileProvider类并在这里使用完全限定的类名。

  • android:authorities 需自定义,是用来标识该 provider 的唯一标识,建议结合包名来保证 authority 的唯一性

    Set the android:authorities attribute to a URI authority based on a domain you control; for example, if you control the domain mydomain.com you should use the authority com.mydomain.fileprovider

  • android:exported 必须设置成 false,否则运行时会报错 java.lang.SecurityException: Provider must not be exported

    the FileProvider does not need to be public

  • android:grantUriPermissions 用来控制是否允许临时授予文件的访问权限,必须设置成 true
  • meta-data 节点
    • android:name 固定写法。
    • android:resource 指定共享文件的路径,此文件放在res/xml/

配置 resource

文件内容完全照抄就行了,鳖折腾。

res/xml/下添加file_paths.xml配置文件

<?xml version="1.0" encoding="utf-8"?>
<paths><files-pathname="files"path="/"/><cache-pathname="cache"path="/"/><external-pathname="external"path="/"/><external-files-pathname="external_file_path"path="/"/><external-cache-pathname="external_cache_path"path="/"/><!--<external-media-pathname="external-media-path"path=""/>-->
</paths>

可配置的元素:

  • files-path 对应内部存储目录 Context.getFilesDir()
  • cache-path 对应内部存储目录 Context.getCacheDir()
  • external-path 对应 Environment.getExternalStorageDirectory()
  • external-files-path 对应 Context.getExternalFilesDir()
  • external-cache-path 对应 Context.getExternalCacheDir()
  • external-media-path 对应 Context.getExternalMediaDirs()

系统提供的各种文件路径

很少会用到的路径:

String downloadCache = Environment.getDownloadCacheDirectory().getAbsolutePath(); //【/cache】
String data = Environment.getDataDirectory().getAbsolutePath(); //【/data】
String root = Environment.getRootDirectory().getAbsolutePath(); //【/system】

SD卡上的路径

String ext = Environment.getExternalStorageDirectory().getAbsolutePath(); //【/storage/emulated/0】
String extDowmload = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); //【/storage/emulated/0/Download】
String extDcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(); //【/storage/emulated/0/DCIM】

data/包名下的路径:

String filesPath = getFilesDir().getAbsolutePath(); //【/data/user/0/包名/files】
String cachePath = getCacheDir().getAbsolutePath(); //【/data/user/0/包名/cache】
String extCachePath = getExternalCacheDir().getAbsolutePath(); //【/storage/emulated/0/Android/data/包名/cache】String extFileDowmloadPath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();//【/storage/emulated/0/Android/data/包名/files/Download】
String extFilesDCIMPath = getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath();//【/storage/emulated/0/Android/data/包名/files/DCIM】

使用案例

安装指定路径的apk

记得要申请权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

示例代码:

public class MainActivity extends AppCompatActivity {private File apkFile;public static final String FROM_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.apk";protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);apkFile = new File(FROM_PATH);findViewById(R.id.tv).setOnClickListener(v -> {//在6.0的华为手机上不能安装【getFilesDir()】目录下的安装包,但可以安装【getExternalStorageDirectory()】下的安装包//而在8.0的小米手机上既可以安装【getFilesDir()】目录下的安装包,也可以安装【getExternalStorageDirectory()】下的安装包File fileDir = new File(getFilesDir(), "bqt");if (!fileDir.exists()) fileDir.mkdirs();apkFile = new File(fileDir, "temp2.apk");copyFile(new File(FROM_PATH), apkFile);});findViewById(R.id.tv2).setOnClickListener(v -> installApk(this, apkFile));}public static void copyFile(File from, File to) {try {FileInputStream fis = new FileInputStream(from);FileOutputStream fos = new FileOutputStream(to);byte[] buf = new byte[1024];int len;while ((len = fis.read(buf)) != -1) {fos.write(buf, 0, len);}fis.close();fos.close();} catch (IOException e) {e.printStackTrace();}}private static void installApk(Context context, File file) {Intent intent = new Intent(Intent.ACTION_VIEW);Uri uri;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);//【content://{$authority}/external/temp.apk】或【content://{$authority}/files/bqt/temp2.apk】} else {intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//【file:///storage/emulated/0/temp.apk】uri = Uri.fromFile(file);}Log.i("bqt", "【Uri】" + uri);intent.setDataAndType(uri, "application/vnd.android.package-archive");context.startActivity(intent);}
}

拍照并指定保存位置

记得要申请权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>

示例代码:

public class MainActivity extends Activity {private int REQUEST_CODE_CAMERA = 10086;private File tempFile;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.tv).setOnClickListener(v -> {//TMLGB,在6.0的华为手机上,使用 getFilesDir() 铁定失败//在8.0的小米6上,使用Environment.getExternalStorageDirectory()也同样失败File fileDir = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? getFilesDir() : Environment.getExternalStorageDirectory();fileDir = new File(fileDir, "bqt");if (!fileDir.exists()) fileDir.mkdirs();tempFile = new File(fileDir, "temp");showCamera(this, tempFile, REQUEST_CODE_CAMERA);});}@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) {Log.i("【bqt", tempFile.getAbsolutePath());//【data/user/0/包名/files/bqt/temp】或【/storage/emulated/0/bqt/temp】}}public static void showCamera(Activity activity, File file, int requestCode) {Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Uri uri;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {String authority = activity.getPackageName() + ".fileprovider"; //【清单文件中provider的authorities属性的值】uri = FileProvider.getUriForFile(activity, authority, file);} else {uri = Uri.fromFile(file);}Log.i("bqt", "【uri】" + uri);//【content://{$authority}/files/bqt/temp】或【file:///storage/emulated/0/bqt/temp】intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);activity.startActivityForResult(intent, requestCode);}
}

2018-8-28

转载于:https://www.cnblogs.com/baiqiantao/p/9548146.html

FileProvider N 7.0 升级 安装APK 选择文件 拍照 临时权限 MD相关推荐

  1. Android 7.0下载安装APK

    Android 7.0下载安装APK 这里只说安装,不说下载.因为下载网上一搜一大堆,暂且说下一下Android7.0 下载安装APK时的兼容性问题. 简介 随着Android版本越来越高,Andro ...

  2. 华为android7.0 root,android7.0更新安装apk的方法

    StrictMode模式 从android7.0开始强制启用StrictMode"严苛模式".StrictMode是在android2.3引进的类.当时它的作用是作为一个开发工具用 ...

  3. Android 7.0以上安装APK的问题

    创建一个类继承FileProvider.在Androidmanifest中注册这个类: <provider android:name=".***.MyFileProvider" ...

  4. android 10.0 禁止安装apk

    连接usb调试adb logcat | grep START 点击应用获取到包名后,去到对应路径对想要屏蔽的apk做判断,如果系统识别到pkgname1里的包名后返回无法安装报错 frameworks ...

  5. Android7.0 PackageManagerService (3) APK安装

    在本篇博客中,我们分析一下Android中的APK是如何安装的,以及PKMS在这个过程中进行了哪些工作. APK的安装方式有很多,我们先来看看如何用adb命令进行安装. 我们从adb install开 ...

  6. AppUtils【获取手机的信息和应用版本号、安装apk】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 一个获取设备的系统版本号.设备的型号.应用版本号code值.应用版本号name值.包名.是否更新.安装apk的工具类. 其实这个工具 ...

  7. android apk安装代码,Android安装APK

    7.0以上安装APK,请自行配置FileProvider,具体不多说 android:name="androidx.core.content.FileProvider" andro ...

  8. ansys18安装以后打不开_【偷偷告诉你】在微信里直接安装apk.1

    相信不少小伙伴都遇到过这样一个问题: 由于一些原因,在微信里收到好友发来的apk文件会微信会自动改名为apk.1,改名后的文件由于手机无法识别,从而无法安装. 从电脑微信上发到文件传输助手也是一样. ...

  9. macOS 13.0 Cocoapods安装 (you don‘t have write permissions for the /Library/Ruby/Gems/2.6.0 directory)

    ❓You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory 安装Cocoapods出现文件夹权限问题 尝试 ...

最新文章

  1. 你知道吗?OAuth2客户端有两种,认证方式有七种。
  2. python【力扣LeetCode算法题库】18-四数之和
  3. 数据结构——树、森林和二叉树之间的转换
  4. 插入数据,已存在则不插入
  5. python解决一些错误换行问题
  6. 手动读取MNIST数据集
  7. 开关型直流电源比线性直流电源效率高的原因?
  8. BP神经网络(BPNN)
  9. 获取连接设备的SN号
  10. 在Proteus上进行LED流水灯的仿真
  11. 舒尔特方格训练注意力游戏
  12. 简述java重载和重写的相同点_方法的重载和重写的异同之处
  13. 钉钉微应用PC开发版调试
  14. 解决微信页面加载自动播放音乐
  15. python基本库six_Python-six库的导入问题
  16. android 7.0 新功能介绍(Nougat)
  17. 正式员工、合同工和外包人员有什么区别?
  18. mysql cloud ha_MySQL HA
  19. TCP/IP 协议详解内容总结
  20. AC的LAN3接口我在WEB里改为三层模式后,再改回二层就不能供电了 AP起不来了怎么恢复?

热门文章

  1. java打印条形码Code128C
  2. call和apply的作用和不同
  3. hdu 3530 单调队列 **
  4. andorid之帧布局FrameLayout
  5. LeetCode(908)——最小差值 I(JavaScript)
  6. python做定时任务的方式及优缺点_使用Python做定时任务及时了解互联网动态
  7. python复制文件到指定文件夹_python 拷贝文件夹下所有的文件到指定文件夹(不包括目录)...
  8. LeetCode58. 最后一个单词的长度
  9. 发生了未经处理的异常
  10. 小米MIX4曝光,颜值真不错