一、FileUriExposedException
问题描述
Android7.0 (N) 开始,严格执行 StrictMode 模式,对安全做更严格的校验。不允许在 App 间,使用file://的方式,传递一个 File ,否者会抛出 FileUriExposedException的异常,会直接引发 Crash。这是由于谷歌认为目标app可能不具有文件权限,会造成潜在的问题。有2种方式来解决这个问题,谷歌官方推荐的方案是FileProvider ,通过FileProvider生成一个 content:// 格式的URI。
FileProvider是android support v4包提供的,继承ContentProvider,便于将自己app的数据提供给其他app访问。在app开发过程中需要用到FileProvider的主要有:
- 相机拍照以及图片裁剪
- 调用系统应用安装器安装apk(应用升级)
FileProvider具体使用流程
1、配置AndroidManifest.xml文件
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
authorities:一个标识,在当前系统内必须是唯一值,一般用包名。
exported:表示该 FileProvider 是否需要公开出去。
granUriPermissions:是否允许授权文件的临时访问权限。
2、创建paths配置文件
在res下新建xml目录,创建一个file_paths.xml文件,文件名可以随意写,和AndroidManifest中配置的一致即可。
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_root"
path="." />
<files-path
name="files-path"
path="." />
<cache-path
name="cache-path"
path="." />
<!--/storage/emulated/0/Android/data/...-->
<external-files-path
name="external_file_path"
path="." />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
<external-cache-path
name="external_cache_path"
path="." />
<!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录,否则微信分身保存的图片,就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg,在小米6的手机上微信分身有这个crash,华为没有
-->
<root-path
name="root-path"
path="" />
/paths>
root-path 对应DEVICE_ROOT,也就是File DEVICE_ROOT = new File("/"),即根目录,一般不需要配置。 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()获取到的目录。
| tag | value | path |
|---|---|---|
| TAG_ROOT_PATH | root-path | / |
| TAG_FILES_PATH | files-path | /data/data/<包名>/files context.getFilesDir() |
| TAG_CACHE_PATH | cache-path | /data/data/<包名>/cache context.getCacheDir() |
| TAG_EXTERNAL | external-path | /storage/emulate/0 Environment.getExternalStorageDirectory() |
| TAG_EXTERNAL_FILES | external-files-path | /storage/emulate/0/Android/data/<包名>/files Context.getExternalFilesDir() |
| TAG_EXTERNAL_CACHE | external-cache-path | /storage/emulate/0/Android/data/<包名>/cache Context.getExternalCacheDir() |
| TAG_EXTERNAL_MEDIA | external-media-path | /storage/emulate/0/Android/media/<包名> context.getExternalMediaDirs() |
3、注册FileProvider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
authorities: 一个标识,在当前系统内必须是唯一值,一般使用包名
exported: 表示该FileProvider是否公开出去
grantUriPermissions: 是否允许授权文件的临时访问权限
绕过Uri检测
在application中添加如下代码,也可以实现绕过Uri检测,在7.0以上的系统通过file:// 的形式共享
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();
}
二、APK signature scheme v2
Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 会使用 APK Signature Scheme v2 和传统签名方案来签署您的应用。
说明**
-
只勾选V1签名就是传统方案签署,但是在 Android 7.0 上不会使用V2安全的验证方式。
-
只勾选V2签名7.0以下会显示未安装,Android 7.0 上则会使用了V2安全的验证方式。
-
同时勾选V1和V2则所有版本都没问题。
三、org.apache不支持问题
build.gradle里面加上
defaultConfig {
useLibrary 'org.apache.http.legacy'
}
或者在AndroidManifest.xml添加下面的配置
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
四、SharedPreferences问题
从Android 7.0开始不能使用MODE_WORLD_READABLE模式创建SharedPreferences文件
SharedPreferences read = getSharedPreferences(RELEASE_POOL_DATA, MODE_WORLD_READABLE);
/**
* File creation mode: allow all other applications to have read access to
* the created file.
* <p>
* Starting from {@link android.os.Build.VERSION_CODES#N}, attempting to use this
* mode throws a {@link SecurityException}.
*
* @deprecated Creating world-readable files is very dangerous, and likely
* to cause security holes in applications. It is strongly
* discouraged; instead, applications should use more formal
* mechanism for interactions such as {@link ContentProvider},
* {@link BroadcastReceiver}, and {@link android.app.Service}.
* There are no guarantees that this access mode will remain on
* a file, such as when it goes through a backup and restore.
* @see android.support.v4.content.FileProvider
* @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION
*/
@Deprecated
public static final int MODE_WORLD_READABLE = 0x0001;