减法,减法,继续减法,因为前俩年做物联网设备,设备Android版本普遍都在5.0左右,所以很少有版本兼容适配的需求;但是在逝去的那一年,我也做过和了解过一点关于10.0的适配工作,索性现在有机会就一起记录吧

版本兼容

  • Android 7.0 兼容适配
  • Android 8.0 兼容适配
  • Android 9.0 兼容适配
  • Android 10.0 兼容适配
  • 分区存储
  • 设备信息
    • IMEI等设备信息
    • Mac地址随机分配
    • 如何标识设备唯一性
  • 权限问题
    • 在后台运行时访问设备位置信息需要权限
    • 一些电话 API、蓝牙 API 和 WLAN API 需要精确位置权限
  • 后台启动 Activity 的限制

Android这些年的不断更新,一直从几个方面去入手,首先是每个版本带来的新特性,其次是越来越注重用户的隐私空间,最后当然是性能的不断提升了

Andoird10主要有行为变更隐私权变更

行为变更 分为以 API 29 及更高级别为目标平台的应用、所有应用

以 API 29 及更高级别为目标平台的应用

  • 有关限制非 SDK 接口的更新
  • 共享内存
  • 移除了应用主目录的执行权限
  • Android 运行时只接受系统生成的 OAT 文件
  • 在 ART 中强制要求 AOT 正确性
  • 针对全屏 Intent 的权限变更
  • 支持可折叠设备
  • java.io.FileChannel.map()更改

所有应用

  • 限制非 SDK 接口
  • 手势导航
  • NDK
  • Bionic 库和动态链接器路径变更
  • 系统二进制文件/库会映射到只执行内存
  • WLAN 直连广播
  • WLAN 感知功能
  • Go 设备上的 SYSTEM_ALERT_WINDOW
  • 关于以旧版 Android 系统为目标平台的应用的警告
  • 移除了 SHA-2 CBC 加密套件
  • 应用使用情况
  • HTTPS 连接变更
  • android.preference 库已弃用
  • ZIP 文件实用程序库变更
  • 摄像头变更
  • 电池用量跟踪
  • Android Beam 已弃用

安全方面

  • TLS 1.3 默认处于启用状态
  • TLS 不信任使用 SHA-1 签名的证书
  • KeyChain 行为变更和改进
  • 其他 TLS 和加密更改

隐私权变更

看了挺多人把Android10.0的内容全量抄了一次,我在此仅记录在开发中常见的适配问题,如有新需求可看官方文档,毕竟官方是老大

在别人的 blog 看到一些有意思的东西,是有关于华为市场对Andoird app版本的一些要求

  • 华为应用市场在2019.3.26日发布了一则公告,要求华为市场上的App在2019年5月底前完成Android Q版本适配工作并自检通过,针对未适配或在Android Q版本体验欠佳的应用,华为应用市场将在Android Q版本机型上采取下架、不推荐更新或屏蔽策略,这一点还需要所有开发者注意了。
  • 华为应用市场在2019.4.2日发布了一则公告,自2019年5月1日起,华为应用市场新上架应用应基于Android 8.0 (API等级26,即targetSdkVersion大于等于26)及以上开发。自2019年8月1日起,现有应用的更新应基于Android 8.0 (API等级26,即targetSdkVersion大于等于26)及以上开发。
    • 2019年5月1日后,未达到要求的新应用,华为应用市场将拒绝上架。2019年8月1日后,未达到要求的现有应用,华为应用市场将拒绝更新。如您的应用API等级小于26,请尽快完成应用的升级改造。

分区存储

核心适配:不同资源放在不同的存储位置,不允许修改三方应用数据,一改之前混乱的存储方式

关于分区存储(Scoped Storage)的概念,我认为从长远角度来看是非常棒的!我觉得Android越来越严谨了,越来越重视用户体验,当然也在不断的追赶着IOS,毕竟Android拥有更多的可能性

在分区存储未出现之前,我们通常获取应用存储权限后,肆意存储数据,导致数据混乱,尤其存储的部分数据并未因app的卸载而消失,反倒成了系统的负担~

了解分区存储的基础关键,首先要了解内存中什么是内部存储?什么是外部存储?这本质上并不是三言两语能说清楚的,所以早之前我有写过一篇 内部存储和外部存储到底是什么?

注意:在适配AndroidQ的时候还要兼容Q系统版本以下的,使用SDK_VERSION区分

Andoird Q 分区存储,与以往有何区别?为我们带来了什么?

首先Android Q文件存储机制修改成了沙盒模式,导致APP只能访问自己目录下的文件和公共媒体文件,那么从前、现在、未来有何不同?

  • android 9(P)及以下系统未做分区存储,还是使用老的文件存储方式,除其他应用的内部存储空间不可以读写,其他任意存储目录下的资源文件都可以正常读写操作。
  • android 10(Q)仅对targetSdkVersion>=29会开启分区存储,所以应用需要提前确保支持分区存储。targetSdkVersion<29则不会有任何限制与android9及以下同理。
  • andorid 11 强制执行分区存储。不允许应用读写操作非应用沙盒目录和系统公共目录下的资源文件。

嗯… 其实…按理应该是先讲各种子项知识点,例如私有目录、公共目录、系统目录等等,但是我还是想先把分区存储的影响范围和适配方式写出来·

受影响的变更,需适配范围

图片位置信息

一些图片会包含位置信息,因为位置对于用户属于敏感信息, Android 10应用在分区存储模式下图片位置信息默认获取不到,应用通过以下两项设置可以获取图片位置信息:

  • manifest中申请ACCESS_MEDIA_LOCATION
  • 调用MediaStore setRequireOriginal(Uri uri)接口更新图片Uri

访问数据

MediaStore.Files应用分区存储模式下,MediaStore.Files 集合只能够获取媒体文件信息(图片、音频、视频), 获取不到非media(pdf、office、doc、txt等)文件。

File Path路径访问受影响接口

开启分区存储新特性, Andrioid 10 不能够通过File Path路径直接访问共享目录下资源,以下接口通过File 路径操作文件资源,功能会受到影响,应用需要使用MediaStore或者SAF方式访问。

存储特性Android版本差异概览

开始适配

关于Android10分区存储的适配方式有俩种,一种为兼容适配,一种为版本(文件迁移)适配

兼容适配

关于兼容适配的方式,其实并不可取!因为随着Andorid版本的不断更新,你肯定要适配以后的版本,而且在Android11中已经强制使用分区存储了,除非你确定你app就保持在Android10的版本,否则兼容适配的意义并不大…

应用未完成外部存储适配工作,可以临时以兼容模式运行, 兼容模式下应用申请存储权限,即可拥有外部存储完整目录访问权限,通过Android10之前文件访问方式运行,以下两种方法设置应用以兼容模式运行。

需注意:应用已完成存储适配工作且已打开分区存储开关,如果当前应用以兼容模式运行,覆盖安装后应用仍然会以兼容模式运行,卸载重新安装应用才会以分区存储模式运行

当tagretSDK>=29 (Android10)时,通过AndroidManifest中申明android:requestLegacyExternalStorage="true"来开启兼容模式,关闭分区适配;相当于targetSdkVersion<=29的时候还是以旧的方式运行

<manifest ...>
...
<application android:requestLegacyExternalStorage="true" ... >
...
</manifest>

判断兼容模式接口

//返回值
//true : 应用以兼容模式运行
//false:应用以分区存储特性运行
Environment.isExternalStorageLegacy();

版本(文件迁移)适配

文件迁移是将应用共享目录文件迁移到应用私有目录或者Android10要求的media集合目录

  • 针对只有应用自己访问并且应用卸载后允许删除的文件,需要迁移文件到应用私有目录文件,可以通过File path方式访问文件资源,降低适配成本。
  • 允许其他应用访问,并且应用卸载后不允许删除的文件,文件需要存储在共享目录,应用可以选择是否进行目录整改,将文件迁移到Android10要求的media集合目录。

文件访问兼容性

共享目录文件不能够通过File path方式读取,需要使用MediaStore API或者Storage Access Framework框架进行访问。

适配指导

AndroidQ中使用ContentResolver进行文件的增删改查。

1)获取(创建)私有目录下的文件夹

//在自身目录下创建apk文件夹
File apkFile = context.getExternalFilesDir("apk");

2)创建私有目录文件

生成需要下载的路径,通过输入输出流读取写入


String apkFilePath = context.getExternalFilesDir("apk").getAbsolutePath();
File newFile = new File(apkFilePath + File.separator + "demo.apk");
OutputStream os = null;
try {os = new FileOutputStream(newFile);if (os != null) {os.write("file is created".getBytes(StandardCharsets.UTF_8));os.flush();}
} catch (IOException e) {} finally {try {if (os != null) {os.close();}catch (IOException e1) {}
}

3)创建共享目录文件夹

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {ContentResolver resolver = context.getContentResolver();ContentValues values = new ContentValues();values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);values.put(MediaStore.Downloads.DESCRIPTION, fileName);//设置文件类型values.put(MediaStore.Downloads.MIME_TYPE, "application/vnd.android.package-archive");//注意MediaStore.Downloads.RELATIVE_PATH需要targetVersion=29,//故该方法只可在Android10的手机上执行values.put(MediaStore.Downloads.RELATIVE_PATH, "Download" + File.separator + "apk");Uri external = MediaStore.Downloads.EXTERNAL_CONTENT_URI;Uri insertUri = resolver.insert(external, values);return insertUri;
}else{...
}

4)在共享目录指定文件夹下创建文件

主要是在公共目录下创建文件或文件夹拿到本地路径uri,不同的Uri,可以保存到不同的公共目录中。接下来使用输入输出流就可以写入文件。

重点:AndroidQ中不支持file://类型访问文件,只能通过uri方式访问。

/*** 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法*/
private Uri  createImageUri() {String status = Environment.getExternalStorageState();// 判断是否有SD卡,优先使用SD卡存储,当没有SD卡时使用手机存储if (status.equals(Environment.MEDIA_MOUNTED)) {return getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());} else {return getContext().getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());}
}

5)通过MediaStore API读取公共目录下的文件


if (cursor != null && cursor.moveToFirst()) {do {...int _id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));Uri imageUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, _id);...} while (!cursor.isLast() && cursor.moveToNext());
} else {...
}
// 通过uri获取bitmap
public Bitmap getBitmapFromUri(Context context, Uri uri) {ParcelFileDescriptor parcelFileDescriptor = null;FileDescriptor fileDescriptor = null;Bitmap bitmap = null;try {parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");if (parcelFileDescriptor != null && parcelFileDescriptor.getFileDescriptor() != null) {fileDescriptor = parcelFileDescriptor.getFileDescriptor();//转换uri为bitmap类型bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);}} catch (Exception e) {e.printStackTrace();}finally {try {if (parcelFileDescriptor != null) {parcelFileDescriptor.close();}catch (IOException e) {}}return bitmap;
}

6)使用MediaStore删除文件

context.getContentResolver().delete(fileUri, null, null);

快来科普上面说明的各种目录知识了... 百尺竿头更进一步 !

外部存储被分为应用私有目录以及共享目录

应用私有目录

存储应用私有数据,外部存储应用私有目录对应Android/data/packagename,私有目录文件访问方式与之前Android版本一致,可以通过File path获取资源

系统公共(共享)目录,存储其他应用可访问文件, 包含媒体文件、文档文件以及其他文件:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones、Alarms、Notifications、Podcasts

  • 公共目录的文件在App卸载后,不会删除
  • 通过plus.gallery.pick获取
  • 拥有权限,也能通过路径直接访问

共享目录文件需要通过MediaStore API或者Storage Access Framework方式访问

  • MediaStore API在共享目录指定目录下创建文件或者访问应用自己创建文件,不需要申请存储权限
  • MediaStore API访问其他应用在共享目录创建的媒体文件(图片、音频、视频), 需要申请存储权限,未申请存储权限,通过ContentResolver查询不到文件Uri,即使通过其他方式获取到文件Uri,读取或创建文件会抛出异常
  • MediaStore API不能够访问其他应用创建的非媒体文件(pdf、office、doc、txt等), 只能够通过Storage Access Framework方式访问;

公共目录对照表

  • Downloads 对应的绝对路径 file:///storage/emulated/0/Download
  • Documents 对应的绝对路径 file:///storage/emulated/0/Documents
  • Pictures 对应的绝对路径 file:///storage/emulated/0/Pictures
  • DCIM 对应的绝对路径 file:///storage/emulated/0/DCIM
  • Movies 对应的绝对路径 file:///storage/emulated/0/Movies
  • Music 对应的绝对路径 file:///storage/emulated/0/Music
  • Ringtones 对应的绝对路径 file:///storage/emulated/0/Ringtones

系统公共目录缺陷

  • 系统公共目录仅支持读取媒体文件 如:音频文件、视频文件、图片文件。其他类型文件不支持!!!!
  • 系统公共目录下创建的文件是公用的,你需要确保你的文件命名是惟一的。否则会出现名称对应不上的问题。多数重命名会文件名尾部(i++)处理
  • 系统公共目录不可随意删减。该文件谁创建的谁才有权限删除修改。如果不是当前应用创建的文件是无权权限删改的。
  • 系统公共目录删减文件会被系统认为恶意操作。将会弹通知栏。告诫手机用户当前XXX应用删除了什么文件。

系统公共目录地址的获取来源

这里多数是通过plus.gallery.pick获取到的媒体文件。如图片视频等。多数是DCIM、Pictures、Movies等。其他Ringtones、Documents仅提供绝对路径让开发者对比,实际业务中可能并不会获取到相关路径目录的。

目前业务逻辑会用到系统公共目录多数为分享。需要将图片等资源拷贝到系统公共目录下。别的三方应用才有权读取该文件。进行操作业务逻辑。


设备信息

核心适配:设备标识符

从Android10开始,默认情况下,在搭载 Android 10 或更高版本的设备上,系统会传输随机分配的 MAC 地址。(即从Android 10开始,普通应用已经无法获取设备的真正mac地址,标识设备已经无法使用mac地址)

IMEI等设备信息

从Android10开始普通应用不再允许请求权限android.permission.READ_PHONE_STATE。而且,无论你的App是否适配过Android Q(既targetSdkVersion是否大于等于29),均无法再获取到设备IMEI等设备信息。

受影响的API:

  • targetSdkVersion<29 的应用,其在获取设备ID时,会直接返回null
  • targetSdkVersion>=29 的应用,其在获取设备ID时,会直接抛出异常SecurityException
Build.getSerial();
TelephonyManager.getImei();
TelephonyManager.getMeid()
TelephonyManager.getDeviceId();
TelephonyManager.getSubscriberId();
TelephonyManager.getSimSerialNumber();

如果您的App希望在Android 10以下的设备中仍然获取设备IMEI等信息,可按以下方式进行适配:

<uses-permission android:name="android.permission.READ_PHONE_STATE"android:maxSdkVersion="28"/>
Mac地址随机分配

从Android10开始,默认情况下,在搭载 Android 10 或更高版本的设备上,系统会传输随机分配的 MAC 地址。(即从Android 10开始,普通应用已经无法获取设备的真正mac地址,标识设备已经无法使用mac地址)

如何标识设备唯一性

Google解决方案:如果您的应用有追踪非登录用户的需求,可用ANDROID_ID来标识设备。

  • ANDROID_ID生成规则:签名+设备信息+设备用户
  • ANDROID_ID重置规则:设备恢复出厂设置时,ANDROID_ID将被重置
 String androidId = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);

信通院统一SDK(OAID)

感叹一下:一般也只有大厂才有这个时间、经验、经历去做这些事情,如果只是对这个需求没有严格要求的话,用Google的方案就可以,毕竟简单方便一些…

统一标识依据电信终端产业协会(TAF)、移动安全联盟(MSA)联合推 出的团体标准《移动智能终端补充设备标识规范》开发,移动智能终端补充设备标识体系统一调用 SDK 集成设备厂商提供的接口,并获得主流设备厂商的授权。

移动安全联盟(MSA)组织中国信息通信研究院(以下简称“中国信通院”)与终端生产企业、互联网企业共同研究制定了“移动智能终端补充设备标识体系”,定义了移动智能终端补充设备标识体系的体系架构、功能要求、接口要求以及安全要求,使设备生产企业统一开发接口,为移动应用开发者提供统一调用方式,方便移动应用接入,降低维护成本。

通过以下方法获取到OAID等设备标识之后,即可作为唯一标识使用。

  1. SDK获取

MSA 统一 SDK下载地址 - 移动安全联盟官网

  1. 接入方式
  • 解压miit_mdid_sdk_v1.0.13.rar,
  • 把 miit_mdid_1.0.13.aar 拷贝到项目中,并设置依赖。
  • 将 supplierconfig.json 拷贝到项目 assets 目录下,并修改里边对应 内容,特别是需要设置 appid 的部分。需要设置 appid 的部分需要去对应的厂 商的应用商店里注册自己的 app。
{"supplier":{"xiaomi":{"appid":"***"},"huawei":{"appid":"***"}...}
}
  • 在初始化方法中调用JLibrary.InitEntry
try {JLibrary.InitEntry(FoundationContextHolder.getContext());
} catch (Throwable e) {}
  • 实例化MSA SDK
public static void initMSASDK(Context context){int code = 0;try {code =  MdidSdkHelper.InitSdk(context,true,listener);if (code == ErrorCode.INIT_ERROR_MANUFACTURER_NOSUPPORT){//1008611,不支持的厂商}else if (code == ErrorCode.INIT_ERROR_DEVICE_NOSUPPORT){//1008612,不支持的设备}else if (code == ErrorCode.INIT_ERROR_LOAD_CONFIGFILE){//1008613,加载配置文件失败}else if (code == ErrorCode.INIT_ERROR_RESULT_DELAY){//1008614,信息将会延迟返回,获取数据可能在异步线程,取决于设备}else if (code == ErrorCode.INIT_HELPER_CALL_ERROR){//1008615,反射调用失败}//code可记录异常供分析}catch (Throwable throwable){}
}static IIdentifierListener listener = new IIdentifierListener() {@Overridepublic void OnSupport(boolean support, IdSupplier idSupplier) {try{isSupport  = support;if (null != idSupplier && isSupport){//是否支持补充设备标识符获取oaid = idSupplier.getOAID();aaid = idSupplier.getAAID();vaid = idSupplier.getVAID();}else {...}}catch (Exception e){}}
};

权限问题

核心适配:定位权限

主要根据隐私权变更官方文档,进行自我记录

Android 每个版本对于权限方面,多多少少都有改变,可能新增权限,也可能更严格要求权限的使用

主要适配权限的重大变更,其他的可自行通过官网进行查看

在后台运行时访问设备位置信息需要权限

我们通常访问设备的位置信息时,主要用到的是 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION,而 Android10 针对应用在后台运行时,访问设备位置信息的场景,需要加入 ACCESS_BACKGROUND_LOCATION 危险权限

如何定义应用是否属于后台运行状态?… 如果符合以下场景及为前台状态,反之则为后台状态

  • 属于该应用的 Activity 可见。
  • 该应用运行的某个前台设备已声明前台服务类型为 location。
    要声明您的应用中的某个服务的前台服务类型,请将应用的 targetSdkVersion 或 compileSdkVersion 设置为 29 或更高版本。详细了解前台服务如何继续执行用户发起的需要访问位置信息的操作。

针对访问设备位置的权限问题,除开前后台运行状态外,我们也需要注意Android 10.0之前的设备和Android 10.0及以后的设备

简单的讲,如果我们app运行在10.0场景下的话,无需适配系统会帮我们自行申请,但是超过10.0(包含10.0)就需要进行适配咯!

以 Android 9 或更低版本为目标平台时自动授予访问权限

如果您的应用在 Android 10 或更高版本上运行,但其目标平台是 Android 9(API 级别 28)或更低版本,则该平台具有以下行为:

  • 如果您的应用为 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION 声明了 元素,则系统会在安装期间自动为 ACCESS_BACKGROUND_LOCATION 添加 元素。
  • 如果您的应用请求了 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION,系统会自动将 ACCESS_BACKGROUND_LOCATION 添加到请求中。

在设备升级到 Android 10 后访问

注意:即使在系统自动更新应用对设备位置信息的访问权限之后,用户仍然可以选择更改这种访问权限级别。用户可以选择让应用只能在前台访问位置信息,或者完全撤消使用权。在尝试访问设备的位置信息之前,尤其是在前台服务中,您的应用应检查用户是否仍然允许您的应用接收此类位置信息。

如果用户向您的应用授予对设备位置信息的访问权限(ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION),然后将其设备从 Android 9 升级到 Android 10,则系统会自动更新应用已获取的基于位置信息的那组权限。您的应用在设备升级后接收的那组权限取决于应用的目标 SDK 版本及其定义的权限,如下表所示:

其实官方不推荐你使用申请后台访问权限的方式,因为这样的结果无非就是多请求一个权限,那么这像变更还有什么意义?申请过多的权限,也会造成用户的反感。所以官方推荐使用前台服务来实现,在前台服务中获取位置信息

  1. 首先在清单中对应的service中添加 android:foregroundServiceType="location"
<serviceandroid:name="MyNavigationService"android:foregroundServiceType="location" ... >...
</service>
  1. 启动前台服务前检查是否具有前台的访问权限
 boolean permissionApproved = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;if (permissionApproved) {// 启动前台服务} else {// 请求前台访问位置权限}
一些电话 API、蓝牙 API 和 WLAN API 需要精确位置权限

如果应用以 Android 10 或更高版本为目标平台,则它必须具有 ACCESS_FINE_LOCATION 权限才能使用 WLAN、WLAN 感知或蓝牙 API 中的一些方法。

Tips:如果您的应用在 Android 10 或更高版本平台上运行,但其目标平台是 Android 9(API 级别 28)或更低版本,则只要您的应用已声明 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 权限,您就可以使用受影响的 API(WifiP2pManager API 除外)。

以下部分列举了受影响的类和方法

电话

  • TelephonyManager

    • getCellLocation()
    • getAllCellInfo()
    • requestNetworkScan()
    • requestCellInfoUpdate()
    • getAvailableNetworks()
    • getServiceState()
  • TelephonyScanManager
    • requestNetworkScan()
  • TelephonyScanManager.NetworkScanCallback
    • onResults()
  • PhoneStateListener
    • onCellLocationChanged()
    • onCellInfoChanged()
    • onServiceStateChanged()

WLAN

  • WifiManager

    • startScan()
    • getScanResults()
    • getConnectionInfo()
    • getConfiguredNetworks()
  • WifiAwareManager
  • WifiP2pManager
  • WifiRttManager

蓝牙

  • BluetoothAdapter

    • startDiscovery()
    • startLeScan()
  • BluetoothAdapter.LeScanCallback
  • BluetoothLeScanner
    • startScan()

后台启动 Activity 的限制

应用处于后台时,无法启动Activity:比如点开一个应用会进入启动页或者广告页,一般会有几秒的延时再跳转至首页。如果这期间你退到后台,那么你将无法看到跳转过程。而在之前的版本中,会强制弹出页面至前

我认为该项适配主要是提升用户体验性

从 Android 10 开始,系统会增加针对 从后台启动 Activity 的限制。此项行为变更有助于最大限度地减少对用户造成的中断,并且可以让用户更好地控制其屏幕上显示的内容。只要您的应用启动 Activity 是因用户互动直接引发的,该应用就极有可能不会受到这些限制的影响。

要详细了解从后台启动 Activity 的建议替代方法,请参阅有关如何在应用中 提醒用户注意有时效性的事件 的指南。

常见场景,前台通知无法打开三方应用

因为此项行为变更适用于在 Android 10 上运行的所有应用,所以这一限制导致最明显的问题就是点击推送信息时,有些应用无法进行正常的跳转(具体的实现问题导致)

所以针对这类问题,可以采取PendingIntent的方式,发送通知时使用setContentIntent方法。

适配方案:将app加入应用平台白名单(有点像root)

这句话是什么意思呢?一般能上白名单的应用除自身平台应用外,很多都是大厂或是有合作的相关平台,所以很多app很难进入白名单,同时要注意,很多厂商的白名单如果被拉黑,可能影响就比较大了~

适配方案:创建高优先级通知

如果使用这种方案,建议最好是掌握Android 8.0 兼容适配 和 Android 9.0 兼容适配,因为该方案用到了8.0渠道通知和后台限制,还有9.0前台服务权限的适配

    Intent fullScreenIntent = new Intent(this, CallActivity.class);PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);NotificationCompat.Builder notificationBuilder =new NotificationCompat.Builder(this, CHANNEL_ID).setSmallIcon(R.drawable.notification_icon).setContentTitle("Incoming call").setContentText("(919) 555-1234").setPriority(NotificationCompat.PRIORITY_HIGH).setCategory(NotificationCompat.CATEGORY_CALL)// Use a full-screen intent only for the highest-priority alerts where you// have an associated activity that you would like to launch after the user// interacts with the notification. Also, if your app targets Android 10// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in// order for the platform to invoke this notification..setFullScreenIntent(fullScreenPendingIntent, true);Notification incomingCallNotification = notificationBuilder.build();

Look:在部分手机上,直接设置setPriority无效(或者说以渠道优先级为准)。所以需要创建通知渠道时将重要性设置为IMPORTANCE_HIGH

 NotificationChannel channel = new NotificationChannel(channelId, "xxx", NotificationManager.IMPORTANCE_HIGH);

虽然限制了后台启动Activity,但是终究有一些例外情况

  • 应用具有可见窗口,例如前台 Activity。

  • 应用在前台任务的返回栈中拥有 Activity。

  • 应用在 Recents 屏幕上现有任务的返回栈中拥有 Activity。
    注意:当此类应用尝试启动新的 Activity 时,系统会将该 Activity 放置到应用现有任务的顶部,但不会离开当前可见的任务。当用户稍后返回应用任务时,系统会启动新的 Activity,而不是之前放置在应用任务顶部的 Activity。

  • 应用的某个 Activity 刚在不久前启动。

  • 应用最近为某个 Activity 调用了 finish()。这仅适用于在调用 finish() 时,应用在前台或前台任务的返回栈中拥有 Activity 的情况。

  • 应用具有受系统约束的服务。此情况仅适用于以下服务,这些服务可能需要启动界面:AccessibilityService、AutofillService、CallRedirectionService、HostApduService、InCallService、TileService、VoiceInteractionService 和 VrListenerService

  • 应用中的某个服务受另一个可见应用约束。请注意,绑定到服务的应用必须保持可见,以便后台应用成功启动 Activity。

  • 应用收到系统的 PendingIntent通知。对于服务和广播接收器的挂起 Intent,应用可在该挂起 Intent 发送几秒钟后启动 Activity。

  • 应用收到另一个可见应用发送的 PendingIntent

  • 应用收到它应该在其中启动界面的系统广播。示例包括 ACTION_NEW_OUTGOING_CALLSECRET_CODE_ACTION。应用可在广播发送几秒钟后启动 Activity。

  • 应用通过 CompanionDeviceManager API 与配套硬件设备相关联。此 API 支持应用启动 API,以响应用户在配对设备上执行的操作。

  • 应用是在设备所有者模式下运行的设备政策控制器。示例用例包括完全托管的企业设备,以及数字标识牌和自助服务终端等专用设备。

  • 用户已向应用授予 SYSTEM_ALERT_WINDOW 权限。

注意:在 Android 10(Go 版本)上运行的应用无法获得 SYSTEM_ALERT_WINDOW 权限。

借鉴blog

  • 携程Android 10适配踩坑指南
  • Android 10 适配攻略,你适配了吗?

Android10.0 版本兼容相关推荐

  1. android10.0版本下载,迷你世界最老版本0.10.0-迷你世界原始版本下载-游戏大玩家...

    在尝试了新版的迷你世界之后,超多的小伙伴在寻找老版本,小编今天给大家分享的就是迷你世界最老版本0.10.0,这也是最原始的版本了,整个的画面还是青砖绿瓦的,虽然画面风格是比较的简单,但是有些内容的玩法 ...

  2. android10.0版本下载,安卓10.0系统

    零度软件园提供安卓10系统更新包下载,安卓10.0系统是一款安卓系统的最新版本,该系统版本不仅加入了5G技术的支持并且修复了以往一些系统bug,这样就能够为安卓手机用户带来不错的使用体验. 安卓10. ...

  3. 独家专访:SequoiaDB 3.0 版本正式发布!协议级完整兼容MySQL!

    SequoiaDB 3.0 版本正式发布!协议级完整兼容MySQL! 标签:SequoiaDB 3.0,MySQL,OLTP SequoiaDB巨杉数据库 3.0,在产品GA发布后,经过近半年在金融级 ...

  4. php7废弃了MySQL,关于mysql8.0版本和PHP7不兼容的问题

    新安装了mysql8.0版本.当PHP连接数据库的时候,会出现一个情况就是连接数据库失败,一般来说会出现以下两种情况: 1.报错:PDO::__construct(): Server sent cha ...

  5. Android之通过用户名和密码连接指定wifi热点(兼容Android9.0和Android10.0和addNetwork(wifiNewConfiguration)返回-1问题)

    1 需求 通过用户名和密码连接指定wifi热点,网上的代码乱七八糟,没几个可以用,我这边整理了下,测试了华为Android9.0和小米Android9.0和10.0和OPPO Android9.0 都 ...

  6. html5和极速模式,浅谈360浏览器6.0版本极速模式与兼容模式_蓝戒的博客

    360浏览器升级到6.0版本后对html5实现了全面兼容,于此同时360 6.0版本浏览器提供了两种模式:1.极速模式 2.兼容模式,也就是说360浏览器为双核浏览器.浏览器最核心的部分是渲染引擎(R ...

  7. impdp 不兼容_oracle - Oracle:使用impdp导入数据库转储时出错:Data Pump客户端与数据库版本12.1.0.2.0不兼容 - 堆栈内存溢出...

    我们从Oracle DB v12创建了一个数据库转储. 1使用expdp(导出数据泵)v12.1实用程序,现在我想将其导入另一个DB v12. 1使用impdp(导入数据泵)工具. 对于杂项. 我需要 ...

  8. android10.0(Q) root QCOM-SM6125 user版本打开root权限

    推荐两个性能优化学习地址: Blog androidperformance MTK 6735/6739/6755/6763 android8.1 user版本打开root权限(adb root权限和 ...

  9. android10.0(Q) root MTK 6765 user版本打开root权限(adb root权限和 apk root权限)

    前言 everybody,好久不见,我胡汉三又回来了,android10.0 root 安排!!! 相比较 Android8.1.9.0 而言,Q 版本 的 root变得相当麻烦,10.0 中引入了动 ...

最新文章

  1. Java Web 程序员的发展趋势分析
  2. HDU 1754 I Hate It(线段树)
  3. 动态规划求一个序列的最长回文子序列(Longest Palindromic Substring )
  4. 双向链表示意图_java双向链表示意图
  5. Flutter高级第1篇:ListView嵌套GridView、不同终端屏幕适配方案
  6. 如何使用串口来给STM32下载程序
  7. 【Java】URL下载网络资源(CloudMusic)
  8. 【JSP】错误页面处理
  9. 举例 微积分 拉格朗日方程_理解微积分真谛:微分方程
  10. 机器学习十大算法!入门看这个就够了~
  11. Tomcat介绍和MyEclipse搭建DRP系统
  12. CSS线性渐变网站记录
  13. word插入页码问题解决办法
  14. 计算机组装与维修论文摘要,计算机组装与维护—本科毕业设计论文摘要.doc
  15. 解决网页打开慢/正在解析主机问题
  16. P2178 后缀数组 + 并查集
  17. TensorFlow 入门到进阶,如何快速学习?
  18. codeforces 438D
  19. 计算机二级word插入图表,四步教你在excel柱形图上添加折线图
  20. 关于springdatajpa 注解@CreatedBy@LastModifiedBy@Version@LastModifiedDate的使用

热门文章

  1. 接口Mock测试结合postman运用
  2. Java 模拟多线程并发案例
  3. Python有趣的微信好友数据分析
  4. PHP-JAVA-Python-JavaScript框架介绍CVE-2018-1002015/CNVD-2018-24942/2x-rce/Spring命令执行/CVE-2021_21234漏洞复现
  5. 【嵌入式基础小知识】详解单片机中的程序和变量是如何分布的以及一些注意事项
  6. 在远程服务器上调用windows(例如office的word、ppt,Surfer绘图软件等)组件时,遇到问题,解决方法记录
  7. python均线选股_GitHub - pkujf/Wind_Python: 量化开发 多因子选股模型
  8. 手机游戏投屏软件有哪些 适合安卓用户
  9. 痞子衡单片机排行榜(2022Q4)
  10. 在手机上安装linux系统