Android 7.0适配

Android 7.0 行为变更,涉及:电池和内存、后台优化、权限更改、NDK 应用链接至平台库。

作为开发者,我们要关注 权限更改、NDK私有库的问题,适配工作也是围绕这2者展开。

分享私有文件的方式

传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider

此类问题,我们要看 关注 拍照、覆盖安装。
原先的拍照,我们一般这样写:

1

2

3

4

5

6

7

8

9

10
public static void (Activity tag, String fileName) {

if (!FileUtil.isSDMounted()) {

ToastUtil.showShortToast(tag, "未挂载SD卡....");

return;

}

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(

getExternalStorageDirectory(), fileName)));

tag.startActivityForResult(intent, PHOTO_REQUEST_TAKEPHOTO);

}

如果 适配到 7.0以上(targetSdkVersion >= 24),上面就会出错:FileUriExposedException
Google 也是越来越注重安全的升级了,不让使用这种方式。现在要通过 provider 来进行访问,由 FileProvider 统一去访问相关的目录、文件。
那具体,我们就看看怎么做:

定义FileProvider

在清单文件中,添加 provider:

1

2

3

4

5

6

7

8

9

10

11

12

13

14
<provider

android:name="android.support.v4.content.FileProvider"

android:authorities="com.chinahr.android.m.fileprovider"

android:exported="false"

android:grantUriPermissions="true">

如果需要别的特殊逻辑,你需要自定义 provider 去继承 v4 包下的 FileProvider并添加自己的逻辑代码,

那么这时候,name 就是你自定义的 provider的全路径了

authorities: packageName.fileprovider -->

<meta-data

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/fileprovider" /><!-- file path 配置文件的路径 -->

</provider>

配置路径文件

在固定的目录下,res/xml 目录下,新建自己的 xml。

然后我们开始配置,如何配置呢,我们来看看官方文档的要求:

1

2

3

4

5
<paths xmlns:android="http://schemas.android.com/apk/res/android">

<!-- 可以配置多个 -->

<files-path name="my_images" path="images/"/>

...

</paths>

主要有4种路径:

path dir
files-path Context.getFilesDir()
cache-path Context.getCacheDir)
external-path name=”name” path=”path” Environment.getExternalStorageDirectory()
external-cache-path name=”name” path=”path” Context.getExternalCacheDir()

那我们来个demo:
在 xml 文件中配置:

1

2

3

4

5
<paths>

<external-path name="takephoto" path="tmp" />

<external-path name="apk" path="Download" />

<cache-path name="images" path="/" />

</paths>

代表的含义就是:
在 sd 下的 tmp目录,Download目录下对应 拍照后存储目录、apk下载的目录
在 packageName/cache 目录下存放app里的图片

配置工作,就ok了,现在解决拍照、覆盖安装崩溃的问题:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
public static void startCameraN(Activity tag, String fileName) {

if (!FileUtil.isSDMounted()) {

ToastUtil.showShortToast(tag, "未挂载SD卡....");

return;

}

// 必须和 xml 配置的 path 一致

File file = new File(getExternalStorageDirectory(), "tmp/"+fileName);

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

Uri pictureUri;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

pictureUri = FileProvider.getUriForFile(tag, tag.getPackageName()+".fileprovider", file);

intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);// 加上权限,否则也是要出错的

} else {

pictureUri = Uri.fromFile(file);

}

intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);

tag.startActivityForResult(intent, PHOTO_REQUEST_TAKEPHOTO);

}

这样,我们就适配结束了

对于覆盖安装,同样的方式:

1

2

3

4

5

6

7

8

9

10

11

12
Intent installIntent = new Intent(Intent.ACTION_VIEW);

Uri apkUri;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

apkUri = FileProvider.getUriForFile(BaseAppUpdateActivity.this, getPackageName()+".fileprovider", apkFile);

} else {

apkUri = Uri.parse("file://" + apkFile.toString());

}

installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");

startActivity(installIntent);

//退出整个app

finishAllActivity();

具体可以看看文档:
https://developer.android.com/reference/android/support/v4/content/FileProvider.html?hl=zh-cn

使用 NDK 私有链接库的问题

从 Android 7.0 开始,系统将阻止应用动态链接非公开 NDK 库,这种库可能会导致您的应用崩溃。此行为变更旨在为跨平台更新和不同设备提供统一的应用体验。即使您的代码可能不会链接私有库,但您的应用中的第三方静态库可能会这么做。因此,所有开发者都应进行相应检查,确保他们的应用不会在运行 Android 7.0 的设备上崩溃。如果应用使用原生代码,则只能使用公开 NDK API。

应用可通过以下三种方式尝试访问私有平台 API:

  • 应用直接访问私有平台库。应更新应用以添加该应用的库副本,或使用公开 NDK API。
  • 应用使用一个可访问私有平台库的第三方库。即使您确定应用不会直接访问私有库,您仍应针对此情景测试应用。
  • 应用引用一个其 APK 中未包含的库。例如,如果尝试使用自己的 OpenSSL 副本,但忘记将它与应用的 APK 进行捆绑,则可能会出现此情况。正常情况下,此应用可在包含 libcrypto.so 的 Android 平台版本上运行。不过,此应用在不包含此库的新版 Android(例如,Android 6.0 和更高的版本)上会崩溃。为修复此问题,请确保 APK 捆绑您的所有非 NDK 库。

这个问题呢,我们就得依靠别人来解决了,当然,如果有自己的 ndk 使用到了私有库,也是要解决的。

首先,我先检查下,有哪些库中使用了私有库,我们把 targetSdkVersion 改成 23,运行在 7.0的手机上,APP启动后,我们观察日志:

1

2
as a workaround for http://b/26394120, note that the access will be removed in future releases of Android.

06-05 16:10:28.556 4284-4284/com.chinahr.android.m W/linker: library "libcrypto.so" ("/vendor/lib/libcrypto.so") needed or dlopened by "/data/app/com.chinahr.android.m-1/lib/arm/libgmacs.so" is not accessible for the namespace "classloader-namespace" - the access is temporarily granted as a workaround for http://b/26394120, note that the access will be removed in future releases of Android.

发现有两处警告,都是来源于 libgmacs.so 中。联系别的部门做适配…

还有,此问题,如果项目 targetSdkVersion < 24,则会在 7.0手机上每次启动app时,弹出警告框:

但也可以继续正常运行,但是这个很不友好,需要尽快联系提供者解决

其他 7.0 适配的小问题

加密方式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22
我们在 logcat 中看到:

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  ********** PLEASE READ ************

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  *

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  * New versions of the Android SDK no longer support the Crypto provider.

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  * If your app was relying on setSeed() to derive keys from strings, you

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  * should switch to using SecretKeySpec to load raw key bytes directly OR

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  * use a real key derivation function (KDF). See advice here :

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  * http://android-developers.blogspot.com/2016/06/security-crypto-provider-deprecated-in.html

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  ***********************************

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  Returning an instance of SecureRandom from the Crypto provider

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  as a temporary measure so that the apps targeting earlier SDKs

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  keep working. Please do not rely on the presence of the Crypto

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  provider in the codebase, as our plan is to delete it

06-05 16:10:28.564 4284-4284/com.chinahr.android.m E/System:  completely in the future.

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  ********** PLEASE READ ************

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  *

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  * New versions of the Android SDK no longer support the Crypto provider.

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  * If your app was relying on setSeed() to derive keys from strings, you

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  * should switch to using SecretKeySpec to load raw key bytes directly OR

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  * use a real key derivation function (KDF). See advice here :

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  * http://android-developers.blogspot.com/2016/06/security-crypto-provider-deprecated-in.html

06-05 16:10:28.566 4284-4284/com.chinahr.android.m E/System:  ***********************************

发现 google 对加密方式也有了要求。
名为 Crypto 的 JCA 提供程序已弃用,因为它仅有的 SHA1PRNG 算法为弱加密。应用无法再使用 SHA1PRNG(不安全地)派生密钥,因为不再提供此提供程序。
而且在未来的版本中,如果继续使用Crypto,将会出错。我们以后需要专注。

通知栏

Android 各个版本中,貌似对通知栏都有一定的更改,而且现在也变得很丰富
7.0 的通知栏,改变了样式,增加了小图标,还可以显示通知数量。

自己在玩原生7.0 系统时,发现小图标变成灰色的方块了:

然后,发现在 华为 7.0 手机上,小图标显示又是正常的,应该是华为对通知栏显示做了处理

这时候就比较蹩脚了,暂且这么干吧,我们项目中,使用了小米推送(通知消息),查阅了小米推送的文档:

* 如果app中同时存在名为mipush_notification和mipush_small_notification的drawable文件,则使用mipush_notification的drawable作为通知的大图标,mipush_small_notification的drawable作为通知的小图标。
* 如果app中只存在其中一个drawable文件,则使用该drawable作为通知的图标。
* 如果app中不存在这两个drawable文件,则使用app的icon作为通知的图标。在MIUI中,通知栏图标统一显示为app的icon,不可以定制。

可以添加个小图标,在推送到达时候显示,透明背景的图片,术语叫:带有alpha 通道的图片。

本来也可以通过 自己控制 NotificationManager 来创建显示,如:

1

2

3

4

5

6

7

8

9

10

11
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

Notification notification = builder

.setContentTitle("这是通知标题")

.setContentText("这是通知内容这是通知内容这是通知内容这是通知内容这是通知内容这是通知内容这是通知内容这是通知内容")

.setWhen(System.currentTimeMillis())

.setSmallIcon(R.mipmap.ic_launcher)

//                .setColor(Color.parseColor("#ff0000”))// 设置小图标背景颜色

.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))

.build();

manager.notify(1, notification);

这是我们可以定制的
但是现在的项目比较死,暂时不适用自定义的了
暂时使用小米推送的改动小图标的方式,替换上一个透明背景的logo吧,总比灰色的方块强吧。而且其他app也是同样的显示效果,如上面的图标中的百度淘宝和头条。
有精力的话,使用透传消息,可以自己控制通知栏的样式和显示。



参考:

http://iluhcm.com/2017/03/12/experience-of-adapting-to-android-notifications/
http://yifeng.studio/2017/05/03/android-7-0-compat-fileprovider/

原文:大专栏  Android 7.0适配

原文地址:https://www.cnblogs.com/chinatrump/p/11615223.html

时间: 2024-10-29 03:36:35

Android 7.0适配的相关文章

我们来一探究竟 Android 7.0 适配中 FileProvider 部分

本文标签: Android7.0 FileProvider 由于 Android 7.0 或更高版本的系统在国内手机市场上的占比不是很高,很多 Android 开发人员并没有做 7.0 适配工作,同时测试人员也容易忽视这方面的兼容问题.这导致 7.0 及以上版本的手机用户在使用到应用部分功能时可能出现 App 崩溃闪退.其中,大部分原因都是由项目中使用到 file:// 类型的 URI 所引发的.本文我们便来一探究竟. Android 7.0 权限变更 为了提高私有目录的安全性,防止应用信息的泄

Android 7.0 适配中 FileProvider 部分总结

本文标签: Android7.0 FileProvider 由于 Android 7.0 或更高版本的系统在国内手机市场上的占比不是很高,很多 Android 开发人员并没有做 7.0 适配工作,同时测试人员也容易忽视这方面的兼容问题.这导致 7.0 及以上版本的手机用户在使用到应用部分功能时可能出现 App 崩溃闪退.其中,大部分原因都是由项目中使用到 file:// 类型的 URI 所引发的.本文我们便来一探究竟. Android 7.0 权限变更 为了提高私有目录的安全性,防止应用信息的泄

关于 Android 7.0 适配中 FileProvider 部分的总结

原文地址:http://yifeng.studio/2017/05/03/android-7-0-compat-fileprovider/ 由于 Android 7.0 或更高版本的系统在国内手机市场上的占比不是很高,很多 Android 开发人员并没有做 7.0 适配工作,同时测试人员也容易忽视这方面的兼容问题.这导致 7.0 及以上版本的手机用户在使用到应用部分功能时可能出现 App 崩溃闪退.其中,大部分原因都是由项目中使用到 file:// 类型的 URI 所引发的.本文我们便来一探究竟

Android 7.0动态权限大总结

应公司项目需求,做了下Android 7.0适配.对于我们程序员,适配7.0主要就是对手机本地文件的Uri做转换处理.注意红色字体,意思也就是说对于http开头的等等,非手机存储中的文件就不用管了.Uri.parse("package") 这样的也不用管!!!!! 7.0的适配,就是对手机存储中的私有文件路径的保护,当系统发现你通过intent带走了一个uri,地址是本地的文件,就会限制的.其他的原理普及请搜索其他文章吧,此处略. 下面贴一下使用步骤和我的工具类. 一.需要修改当前mo

Android 根据城市获取经纬度 适配Android 7.0 、Android 8.0

今天在开发的过程中遇到了一个很奇葩的问题,就是之前已经写好的代码在Android 7.0 的系统中运行是正常的,但是在Android8.0 的系统中运行就出现了问题.在网上也找了很多资料,经过自己的不断折腾,终于找到了问题.废话不 多说了,直接上代码:public void getLatAndLng(String cityName) {Geocoder geocoder = new Geocoder(this,Locale.CHINA);try {List<Address> addressLi

1.Android6.0运行时权限简介_2.Android6.0权限适配之WRITE_EXTERNAL_STORAGE(SD卡写入)3_.Android 6.0 运行时权限理解

Android6.0运行时权限简介:http://unclechen.github.io/2016/03/05/Android6.0%E8%BF%90%E8%A1%8C%E6%97%B6%E6%9D%83%E9%99%90%E7%AE%80%E4%BB%8B/ Android6.0权限适配之WRITE_EXTERNAL_STORAGE(SD卡写入):https://blog.csdn.net/xiechengfa/article/details/52699233 Android 6.0 运行时权

android 7.0+ FileProvider 访问隐私文件 相册、相机、安装应用的适配

从 Android 7.0 开始,Android SDK 中的 StrictMode 策略禁止开发人员在应用外部公开 file:// URI.具体表现为,当我们在应用中使用包含 file:// URI 的 Intent 离开自己的应用时,程序会发生FileUriExposedException 异常 这里我们要使用到的 FileProvider,就是 ContentProvider 的一个特殊子类,帮助我们将访问受限的 file:// URI 转化为可以授权共享的 content:// URI.

Android 8.0+ 通知不显示的适配

最近在 写项目的时候  发现 通知并不会显示的问题,查看资料发现 从Android 8.0开始通知必须加上ChannelId Android O 引入了 通知渠道(Notification Channels),以提供统一的系统来帮助用户管理通知,如果是针对 android O 为目标平台时,必须实现一个或者多个通知渠道,以向用户显示通知.比如聊天软件,为每个聊天组设置一个通知渠道,指定特定声音.灯光等配置 String id = "my_id"; String name="m

Android 9.0网络权限适配

在做Android开发时,使用华为的p20和平板(均为Android 9.0)测试时,发现不能使用WIFI网络,一番郁闷纠结查找后 直接上方法: 在res文件夹下创建xml文件夹,在xml里面创建文件network_security_config.xml <?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTraff