安卓compileSdkVersion ;=25导致的问题汇总

Posted CodingForAndroid

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓compileSdkVersion ;=25导致的问题汇总相关的知识,希望对你有一定的参考价值。

升级了 compileSdkVersion 与 targetSdkVersion 到25, 导致app升级安装包 无法安装,报错日志UriExposedException。

问题1.应用间共享文件 直接使用绝对路径,会报错UriExposedException(android7.0系统 调用系统相机、系统播放器播放视频、切图兼容问题,报异常android.os.FileUriExposedException)。

问题1解决方案:FileProvider (点这里关于FileProvider的详情介绍
FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri.

对于面向 Android N 的应用,Android 框架执行的 StrictMode,API 禁止向您的应用外公开 file://URI。
如果一项包含文件 URI 的 Intent 离开您的应用,应用失败,并出现 FileUriExposedException异常。

若要在应用间共享文件,您应发送一项 content://URI,并授予 URI 临时访问权限。
进行此授权的最简单方式是使用 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_provider_paths"/>
</provider>

在清单文件中注册FileProvider.
接下来在 项目的主目录下src/main/res/xml 文件夹下面创建file_provider_paths.xml

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="download_dir" path="downloads" />
    <files-path name="my_images" path="images/"/>
    <files-path name="xxx" path="./"/>
   ...
</paths>

paths中共有以下几种类型

The <paths> element must contain one or more of the following child elements:

<files-path name="name" path="path" />
Context.getFilesDir(). 
对应目录 /data/user/0/packageName/files

<cache-path name="name" path="path" />
getCacheDir().
对应目录 /data/user/0/packageName/cache

<external-path name="name" path="path" />
Environment.getExternalStorageDirectory().
对应目录 /storage/emulated/0

<external-files-path name="name" path="path" />
Context.getExternalFilesDir(null)
对应目录 /storage/emulated/0/Android/data/packageName/files
Context.getExternalFilesDir("images")
对应目录 /storage/emulated/0/Android/data/packageName/files/images

<external-cache-path name="name" path="path" />
Context.getExternalCacheDir().
对应目录 /storage/emulated/0/Android/data/packageName/cache

<external-media-path name="name" path="path" />
Context.getExternalMediaDirs().
对应目录 /storage/emulated/0/Android/media/packageName

这几张类型都有相同的属性 name 与 path ,

name="name"
一个URI路径段。为了保障隐私,这个值隐藏你共享的子目录的名称。这个值的子目录名称包含在路径属性上。
path="path"
你共享的子目录。虽然名称属性是一个URI路径,路径的值是一个真实的子目录的名称。注意,path的值是指一个子目录,而不是具体的单个或多个文件。你不能共享单个文件的文件名,也不可以使用通配符指定文件的一个子集。

对文件生成 Content URI

使用content URI 分享一个文件给别的app,需要在自己的app 中生成一个content URI 。生成content URI,首先创建一个新文件,然后通过getUriForFile()传递文件。可以使用intent把通过 getUriForFile() 返回的 content URI 发送给另一个app。
接收的客户端应用程序可以打开文件并通过调用ContentResolver.openFileDescriptor来获得一个ParcelFileDescriptor访问其内容。
例如,假设你的app 需要通过使用权限为 com.mydomain.fileprovider的FileProvider提供文件给三方的app。
为了获取 内部存储子目录images下的default_image.jpg的content URI需要添加以下代码:

File imagePath = new File(getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = FileProvider.getUriForFile(this, "com.mydomain.fileprovider", newFile);

作为之前片段的结果getUriForFile() 返回 content URI 为:content://com.mydomain.fileprovider/my_images/default_image.jpg.

给一个URI临时权限
为了授权一个使用权限给 通过 getUriForFile()返回的content URI,按下列步骤操作:
1.为content:// Uri调用方法 grantUriPermission(String toPackage, Uri uri, int modeFlags)使用需要的模式标记. 为content URI授予临时权限给自定的包, 相应的mode_flags 可选参数FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION or both. 这些权限会一直保持直到你调用revokeUriPermission()撤销权限或者设备重启。

2.通过调用Intent 的setData()设置content URI。

3.接下来调用Intent.setFlags()设置Flags 可以为FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION or 两个同时设置.

4.最后,发送Intent 给另一个app。大多时候,通常操作是调用setResult()。
当Activity栈中接收Intent 中的Activity存活时,权限许可会一直有效。当栈结束,权限自动移除。app程序中一个Activity钟授予的权限就自动的扩展到该app其他组件的。

为三方应用提供Content URI
有多种方式可以为一个三方应用提供文件的 content URI。一个通常的方式是 在你的app 中调用startActivityResult(),发送一个Intent 给你的app 来在你的app 中启动一个Activity。接下来,你的app可以立即返回一个content URI给三方app或者提供一个用户接口允许使用者去获取该文件。接下来,一但用户获取了该文件,并使用setResult() 发送一个Intent,你的app可以收到一个content URI的通知。

也可以把content URI放到 ClipData对象,然后将对象添加到一个Intent发送到三方的app中。要做到这一点,调用Intent.setClipData()。当你使用这种方法,您可以添加多个ClipData对象给Intent,每个都有自己的内容URI。当你调用Intent.setFlags()来设置临时访问权限,相同的权限许可同样应用到了全部的相同的content URIs。

Note: The Intent.setClipData() method is only available in platform version 16 (Android 4.1) and later. 
If you want to maintain compatibility with previous versions, you should send one content URI at a time in the Intent. 
Set the action to ACTION_SEND and put the URI in data by calling setData().

以打开相机为例具体如下:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
    File file = new File(filePath);
    Uri contentUri = FileProvider.getUriForFile(context,"com.womai.fileProvider",file);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
else 
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(filePath)));

if (null != intent.resolveActivity(context.getPackageManager())) 
    context.startActivityForResult(intent, Constants.ResultCode.BLESS_TAKEPHOTO);

这里就通过getUriForFile方法将通过Intent 对外暴露的 Uri做了转换。

以app升级安装为例:

安装:
Intent intent = new Intent();
// 执行动作
intent.setAction(Intent.ACTION_VIEW);

if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
	intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
	Uri contentUri = FileProvider.getUriForFile(context,"com.womai.fileProvider",file);
	intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
else 
	// android 4.0以后需要这句话,要不然不显示安装成功
	intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	intent.setDataAndType(
			Uri.fromFile(file),
			"application/vnd.android.package-archive");

context.startActivity(intent);
欢迎爱学习的小伙伴加群一起进步:230274309

以上是关于安卓compileSdkVersion ;=25导致的问题汇总的主要内容,如果未能解决你的问题,请参考以下文章

安卓Mannifest.xml文件说明

设置安卓构建全局环境变量

记录打包遇到的问题

iMindMap 10怎么用

compileSdkVersion 和 targetSdkVersion 有啥区别?

25 RocketMQ原理思维导图与扩展思考