Android不同版本特性知识点

不同版本特性

版本特性
6.0动态权限
7.01、应用间共享文件
禁止使用 file://URI ,否则抛出 FileUriExposedException 异常
在应用间共享文件,需要通过 content://URI 并授予 URI 临时访问权限
实现:
自定义 MyFileProvider extends FileProvider
再 manifest 中 注册该 <provider>,exported 设置为 false
在 <provider> 的 <meta-data> 中配置文件共享目录

2、Apk signature scheme v2
V1签名不会校验 apk文件的注释区
V2签名会校验 apk文件 注释区的是否修改
V2签名增加一个签名区块,签名区块中可根据固定的 id,获取签名信息
签名区块不会校验是否修改

3、Toast导致的BadTokenException
7.1版本(API25),Toast 的 WindowManager.LayoutParams参数新增了一个token属性,
用于对添加的窗口进行校验
UI线程发生阻塞导致 TN.show() 没有及时执行时,NMS 检测超时后会删除 WMS中的该token,即造成token失效

解决方案:反射获取 mTN # mHandler,通过静态代理,在 mHandler 发送消息时加上 try-catch

4、Android 7.0 WebView 部分机型打不开
通过重写 onReceivedSslError() 过滤掉 部分错误

5、多语言特性
增加 LocaleList.getDefault() 获取多语言列表
多语言查找规则:
查找系统设置的第一语言(比如,中文简体)最匹配的资源
没找到则查找第一语言所属语言(比如,通用中文)匹配的资源
没找到则查找第一语言的其他分支语言(比如,中文简体-香港)匹配的资源

如果第一语言的资源都没有找到,查看是否设置多个语言
如果有,查看是否有与第二语言(比如,英国英语)最匹配的资源
如果没有查找第二语言所属的语言(通用英语)匹配的资源
没有则查找第二语言其他分支(美国英语)匹配的资源

如果没有,则查找第三语言、第四语言…的资源

如果都没有则加载默认资源 values/string.xml
如果默认资源也没有就抛异常

6、通知栏适配
1)增加 Notification.DecoratedCustomViewStyle()、Notification.DecoratedMediaCustomViewStyle(),
帮助更好的装饰带有 RemoteViews 的通知栏消息
2)需要动态设置 Builder.setShowWhen(true) 才会显示时间
3)支持 Action的直接回复,通过 RemoteInput 实现,且回复的消息内容支持立即添加到通知栏
4)支持通知消息组,相似的消息在达到一定数量后会按照消息组来显示
5)增加 NotificationManager.areNotificationsEnabled 获取是否开启了通知权限

7、后台优化
删除了三项隐式广播 CONNECTIVITY_ACTION、ACTION_NEW_PICTURE、ACTION_NEW_VIDEO
避免隐式广播在后台频繁启动监听了这些广播的应用,带来很大的电量消耗
应对策略:

JobScheduler API
后系统控制后台任务执行时机

8、PopupWindow位置不正确
1)使用了update方法,同时设置了Gravity
update方法在 7.0 以上会获取 Gravity 进行更新判断
解决方法:不使用update方法

2)PopupWindow 高度为 MATCH_PARENT,调用 showAsLocation 显示时,
PopupWindow 没有在指定控件的下方显示。如果使用showAsDropDown,会全屏显示。
解决方法:
a:指定 PopupWindow 的高度为 WRAP_CONTENT,调用 showAsDropDown 方法展示
b:弹出前,如果是 7.0以上,计算并设置 PopupWindow高度,再调用 showAtLocation 展示
8.01、运行时权限
6.0 之后,8.0 之前,应用运行时请求权限且被授予该权限
系统会错误地将 在清单中注册的、同一权限组的、权限 一起授予应用
(同一权限组的其他权限,用户未申请,也会被授予)

8.0 之后,系统只会授予应用明确请求的权限
(同一权限组的其他权限,用户未申请,不授予。用户申请,则自动授予)

按权限组来申请权限

2、通知适配
8.0 引入通知渠道,需要给通知创建渠道
用户界面将通知渠道称之为通知类别

3、悬浮窗适配
使用 SYSTEM_ALERT_WINDOW 权限的应用,
无法再使用以下窗口类型添加悬浮窗:
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR

必须使用 TYPE_APPLICATION_OVERLAY 的窗口类型

4、安装apk
8.0 去除了“允许未知来源” 选项
App有安装App的功能(检查更新之类的)无法正常安装。

解决方案:
a:首先在 AndroidManifest 中 添加安装未知来源应用的权限:
<uses-permission android:name=“android.permission.REQUEST_INSTALL_PACKAGES”/>
系统会自动询问用户完成授权

b:先使用 canRequestPackageInstalls() 查询是否有此权限
如果没有,则使用 Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES 这个action
将用户引导至安装未知应用权限界面去授权

权限组、悬浮窗、安装未知来源应用的权限适配,可以使用 AndPermission 更加便捷

5、透明主题的 Activity
8.0 出现的bug(官方已经在8.1修复)

只有全屏不透明的activity才可以设置方向。
否则报错:
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

解决办法:
a:去掉 透明 activity 的 <screenOrientation 属性/font>,或者去掉对应设置方向的代码
b:去除透明效果

方案b最好是添加 values-v26目录,单独处理8.0 版本

6、集合的处理
8.0以上 AbstractCollection.removeAll(null) 和 AbstractCollection.retainAll(null)
始终引发 NullPointerException;

在之前,当集合为空时不会引发 NullPointerException。所以我们需要做判空处理。

7、后台执行限制
应用在两个方面受到限制:

后台服务限制
处于空闲状态时,应用可以使用的后台服务存在限制
前台服务受限制

广播限制
除了有限的例外情况,无法使用清单注册隐式广播
它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。

解决方案:
使用 JobScheduler 克服这些限制
JobScheduler 让应用安排为在未活跃运行时执行工作

关于JobScheduler的用法可以参考官方例子:android-JobScheduler
后台任务google推荐方案使用 WorkManager,
WorkManager 内部维护着JobScheduler,自动维护后台任务,
同时满足后台Service和静态广播
在6.0 以下系统版本会自动切换为 AlarmManager


8、后台位置信息限制
为降低耗电量,8.0(API 26)对后台应用获取用户当前位置信息的频率进行限制
应用每小时仅接收几次位置信息更新


前台应用(满足以下任一条件):
a:有可见的 Activity,无论 Activity 处于启动还是暂停状态。
b:有前台服务。
c:另一个前台应用已关联到该应用
(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)

解决方案:
将应用转至前台

调用 startForegroundService() 以启动应用中的某个前台服务
前台服务会以通知的形式持续显示在通知区域
9.01、新特性
利用 Wi-Fi RTT 进行室内定位
刘海屏 API支持
通知栏功能增强
多摄像头支持、摄像头更新
HDR VP9 视频、HEIF 图像压缩、Media API


2、non-SDK接口的使用

Android P 无法通过反射、JNI 间接使用非 SDK 接口

3、挖孔屏适配
有状态栏的页面,不会受到挖孔屏特性的影响;
全屏显示的页面,系统挖孔屏方案会下移应用界面避开挖孔区显示

P版本三方适配挖孔屏方案:
a:新增挖孔屏挖孔尺寸和位置接口

WindowInsets#getDisplayCutout();

b:新窗口布局模式,可获取是否在挖孔区域布局

WindowManager.LayoutParams#layoutInDisplayCutoutMode 值说明:
a)LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
默认模式,全屏窗口不使用挖孔区域,非全屏窗口正常使用挖孔区域。

b)LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:
声明使用挖孔区域

c)LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
声明不使用挖孔区域

4、Battery Improvements 功耗解决方案
AAB(Auto Awesome Battery):
i、通过ML算法将应用进行分类,不同类型的应用功耗管控策略不一样
ii、 Firebase Cloud Messaging (FCM):管控三方消息接收的频率
iii、应用的管控方法:Forced App Standby (FAS),谷歌不会通过清理应用来优化功耗

Extreme Battery Saver(EBS)谷歌超级省电模式;

Smart screen brightness:屏幕亮度调节优化算法。

影响:
谷歌功耗方案对三方应用各种管控,存在导致应用后台功能无法正常使用的可能

5、不允许共享WebView数据目录
不能再跨进程共享单个WebView数据目录,否则会崩溃

6、移除对 Build.serial 的直接访问
需要 Build.serial 标识符 必须请求 READ_PHONE_STATE 权限然后使用 Android P 新增的新 Build.getSerial() 函数
7、SELinux 禁止访问应用的数据目录
不允许直接通过路径访问其他应用的数据目录
可以使用进程间通信 (IPC) 机制(包括通过传递 FD)共享数据

8、常见问题:
1)java.lang.SecurityException: Failed to find provider null for user 0;

问题原因:项目使用了ActiveAndroid,在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时
调用 ContentResolver # notifyChange 方法通知数据更新,
或调用 ContentResolver # registerContentObserver 方法监听数据变化

解决方案:
在清单文件配置
<provider
android:name=“com.activeandroid.content.ContentProvider”
android:authorities=“com.ylmf.androidclient”
android:enabled=“true”
android:exported=“false”>
</provider>

2)onError : CLEARTEXT communication to life.115.com not permitted by network security policy

问题原因:在Android P(API 28)系统的设备上或 targetSdkVersion 为28,默认要求使用加密连接
比如,默认只能使用HTTPS进行通信,若强行使用HTTP通信,则会得到上面的异常

解决方案:
在资源文件新建xml目录,新建文件

<?xml version=“1.0” encoding=“utf-8”?>
<network-security-config>
<base-config cleartextTrafficPermitted=“true” />
</network-security-config>

清单文件的application标签中添加配置:
android:networkSecurityConfig="@xml/network_security_config"

android Q1、定义公共媒体集合:Photos & Videos、Music、 Downloads
操作其他应用已创建的文件,必须首先请求相应的权限
Photos:READ_MEDIA_IMAGES 权限
Videos:READ_MEDIA_VIDEO 权限
Music:READ_MEDIA_AUDIO 权限
Downloads:需要让用户使用系统的文件选择器选择文件

2、访问共享集合通过 MediaStore API
MediaStore.Images、MediaStore.Video、MediaStore.Audio、MediaStore.Downloads

3、外存中私有目录权限变更
获取应用私有目录文件,不再需要权限

4、新增 ACCESS_MEDIA_LOCATION 权限
一些照片在其数据中会包含位置信息

6,存储新特性
识别特定的外部存储设备
Android Q 为每个外部存储设备提供唯一的卷名

//获取卷名方式
Set volumeNames = MediaStore.getAllVolumeNames(context);

7,Activity后台活动限制
至少满足以下一个条件才能启动Activity:
1)该APP具有可见窗口,如,前台Activity
2)前台的另一个 APP 发送该应用程序的 PendingIntent
3)系统发送该 APP 的PendingIntent,如点击通知
4)系统向应用程序发送广播,如,SECRET_CODE_ACTION
只有应用程序预期启动UI的特定广播才免除8.Activity后台活动限制的兼容性

特殊情况如:来电、警报,需要立刻启动 Activity,
可以通过创建高优先级的通知,并提供 fullscreen intent

9.设备位置权限的访问控制
请求地理位置时,会通过对话框的形式给用户进行授权提示
此对话框允许用户授予对两个不同范围的位置访问权限:
在使用中(仅限前台)或始终(前台和后台)

新增权限 ACCESS_BACKGROUND_LOCATION
Android Q 需要在后台运行时访问用户的位置,必须在应用的清单文件中声明该权限
<manifest>
<uses-permission android:name=“android.permission.ACCESS_COARSE_LOCATION” />
<uses-permission android:name=“android.permission.ACCESS_BACKGROUND_LOCATION” />
</manifest>

10.位置限制的兼容性
Android Q 设备运行,但targetSdk < Q,则会出现以下行为:
系统会在安装期间自动添加 ACCESS_BACKGROUND_LOCATION 权限
在请求定位权限时,系统自动增加对 ACCESS_BACKGROUND_LOCATION 权限的申请
用户可以选择只在前台获取位置

11,允许应用程序降级
可以回滚到旧版本

12.对数据和标识符的更改
1)获取联系人信息,结果不再按交互频率排序

2)MAC地址随机化,默认传输随机的MAC 地址
获取随机MAC地址I:WifiConfiguration.getRandomizedMacAddress()
获取实际硬件MAC地址:WifiInfo.getFactoryMacAddress()

3)唯一标识符
需要 READ_PRIVILEGED_PHONE_STATE 权限,才能访问设备的不可重置标识符,包括 IMEI、序列号

4)访问剪贴板数据
除默认输入法程序外,没有焦点的应用无法访问剪贴板数据

5)访问USB串行需要用户许可
targetSdk >= Android Q,只能在用户授予您访问USB设备权限后才能读取序列号

6)相机和连接相关更改
访问相机信息需要获得许可
Android Q更改了 getCameraCharacteristics()方法返回的信息的广度必须具有 CAMERA 权限
才能访问 getCameraCharacteristics()方法的返回值中包含的设备元数据

7)启用、禁用Wi-Fi的限制

无法启用、停用Wi-Fi
WifiManager.setWifiEnabled() 方法始终返回false
只能使用设置面板提示用户启用、禁用Wi-Fi

一、Android不同版本下的特性

//TODO android 10、折叠屏、刘海屏、环绕屏 适配

6.0

动态权限

7.0

1、应用间共享文件

2、Apk signature scheme v2

3、PopupWindow位置不正确

1)我们使用了update方法,同时设置了Gravity(Gravity.NO_GRAVITY没事)。
因为在update方法中有调用computeGravity方法去获取Gravity。
(7.0 以下没有获取Gravity进行更新判断)

解决方法:不使用update方法。

2)PopupWindow 高度为 MATCH_PARENT,在显示的时候调用showAsLocation方法时,
PopupWindow并没有在指定控件的下方显示。如果使用showAsDropDown,会全屏显示。

解决方法:
a:指定 PopupWindow 的高度为 WRAP_CONTENT, 调用 showAsDropDown方法。
b:弹出时做一下判断处理

// Android 7.x中,PopupWindow高度为match_parent时,会出现兼容性问题,需要处理兼容性
if (Build.VERSION.SDK_INT >= 24) { 
	int[] location = new int[2]; // 记录anchor在屏幕中的位置
	anchor.getLocationOnScreen(location);
	int offsetY = location[1] + anchor.getHeight();
	
	// Android 7.1 ,8.0中,PopupWindow高度为 match_parent 时,会占据整个屏幕
	if (Build.VERSION.SDK_INT >= 25) { 
		// 故而需要在 Android 7.1上再做特殊处理
		// 获取屏幕高度
		int screenHeight = ScreenUtils.getScreenHeight(context); 
		// 重新设置 PopupWindow 的高度
		popupWindow.setHeight(screenHeight - offsetY); 
	}
	popupWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, 0, offsetY);
} else {
	popupWindow.showAsDropDown(anchor);
}

4、后台优化
一些隐式广播可以做一些特定的功能,如,当手机网络变成WiFi时自动下载更新包等。
但,这些隐式广播会在后台频繁启动已注册监听这些广播的应用,从而带来很大的电量消耗。

为了提升设备性能和用户体验,在Android 7.0 中删除了三项隐式广播,
以帮助优化内存使用和电量消耗

a:在 Android 7.0上 应用不会收到 CONNECTIVITY_ACTION 广播,
即使你在manifest清单文件中设置了请求接受这些事件的通知。

在前台运行的应用如果使用BroadcastReceiver 请求接收通知,
仍可以在主线程中侦听 CONNECTIVITY_CHANGE

b:在 Android 7.0上应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 类型的广播。

应对策略:
JobScheduler API
提供了一个稳健可靠的机制来安排满足指定条件(例如连入无线流量网络)
时所执行的网络操作。
您甚至可以使用 JobScheduler API 来适应内容提供程序变化。

5、多语言特性

6、通知栏适配

增加了Notification.DecoratedCustomViewStyle()
和Notification.DecoratedMediaCustomViewStyle(),
帮助更好的装饰带有RemoteViews的通知栏消息。

需要动态设置Builder.setShowWhen(true)才会显示时间。
支持Action的直接回复,通过RemoteInput实现,且回复的消息内容支持立即添加到通知栏。
支持通知消息组,相似的消息在达到一定数量后会按照消息组来显示。
增加了NotificationManager.areNotificationsEnabled告知应用是否开启了通知权限

7、WebView 问题

Android 7.0 WebView 部分机型打不开

通过重写  onReceivedSslError 过滤掉 部分错误
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
	if (error.getPrimaryError() == SslError.SSL_DATE_INVALID
			|| error.getPrimaryError() == SslError.SSL_EXPIRED
			|| error.getPrimaryError() == SslError.SSL_INVALID
			|| error.getPrimaryError() == SslError.SSL_UNTRUSTED) {
		handler.proceed();
	} else {
		handler.cancel();
	}
	super.onReceivedSslError(view, handler, error);
}

8、Toast导致的BadTokenException

8.0

1、运行时权限
6.0 之后,8.0 之前,如果应用在运行时请求权限并且被授予该权限,
系统会错误地将【属于同一权限组】并且在清单中注册的其他权限也一起授予应用。

8.0 之后,系统只会授予应用明确请求的权限。
然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

比如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。
应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。

如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,
因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。

如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;
如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。

按权限组来申请权限。

2、通知适配
Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。
用户界面将通知渠道称之为通知类别。

private void createNotificationChannel() {
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
		NotificationManager notificationManager = (NotificationManager)
				getSystemService(Context.NOTIFICATION_SERVICE);
		//分组(可选)
		//groupId要唯一
		String groupId = "group_001";
		NotificationChannelGroup group = new NotificationChannelGroup(groupId, "广告");
		//创建group
		notificationManager.createNotificationChannelGroup(group);
		//channelId要唯一
		String channelId = "channel_001";
		NotificationChannel adChannel = new NotificationChannel(channelId,
				"推广信息", NotificationManager.IMPORTANCE_DEFAULT);
		//补充channel的含义(可选)
		adChannel.setDescription("推广信息");
		//将渠道添加进组(先创建组才能添加)
		adChannel.setGroup(groupId);
		//创建channel
		notificationManager.createNotificationChannel(adChannel);
		//创建通知时,标记你的渠道id
		Notification notification = new Notification.Builder(MainActivity.this, channelId)
				.setSmallIcon(R.mipmap.ic_launcher)
				.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
				.setContentTitle("一条新通知")
				.setContentText("这是一条测试消息")
				.setAutoCancel(true)
				.build();
		notificationManager.notify(1, notification);
	}
}

//8.0删除通知渠道
private void deleteNotificationChannel(String channelId){
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
		NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
		mNotificationManager.deleteNotificationChannel(channelId);
	}

}

3、悬浮窗适配
使用 SYSTEM_ALERT_WINDOW 权限的应用,无法再使用以下窗口类型

来在其他应用和系统窗口上方显示提醒窗口:
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR

应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
	mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}else {
	mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
}

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />

4、安装apk

Android 8.0 去除了“允许未知来源”选项,所以如果我们的App有安装App的功能(检查更新之类的),
那么会无法正常安装。

解决方案:
a:首先在AndroidManifest文件中添加安装未知来源应用的权限:

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

这样系统会自动询问用户完成授权。

b:可以先使用 canRequestPackageInstalls()查询是否有此权限,
如果没有的话使用 Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES 这个action
将用户引导至安装未知应用权限界面去授权。

private static final int REQUEST_CODE_UNKNOWN_APP = 100;

private void installAPK(){
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
		boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
		if (hasInstallPermission) {
			//安装应用
		} else {
			//跳转至“安装未知应用”权限界面,引导用户开启权限
			Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
			Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
			startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP);
		}
	}else {
		//安装应用
	}
}

//接收“安装未知应用”权限的开启结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	if (requestCode == REQUEST_CODE_UNKNOWN_APP) {
		installAPK();
	}
}

对于权限组、悬浮窗还有安装未知来源应用的权限适配,
我们也可以使用 AndPermission 更加便捷。

5、透明主题的Activity
在targetSdk = 27,Android为8.0 的手机时,出现的bug(因为官方已经在8.1修复)

只有全屏不透明的activity才可以设置方向。否则报错如下:
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

解决办法:
a:去掉对应activity中的 screenOrientation 属性,或者对应设置方向的代码。
b:舍弃透明效果,在它的Theme中添加:
false
方案b最好是添加 values-v26目录,单独处理8.0 版本。个人推荐方案a

6、集合的处理

现在,AbstractCollection.removeAll(null) 和 AbstractCollection.retainAll(null)
始终引发 NullPointerException;
之前,当集合为空时不会引发 NullPointerException。所以我们需要做判空处理。

7、后台执行限制

应用在两个方面受到限制:

后台服务限制:
处于空闲状态时,应用可以使用的后台服务存在限制。
这些限制不适用于前台服务,因为前台服务更容易引起用户注意。

广播限制:
除了有限的例外情况,应用无法使用清单注册隐式广播。
它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。

在大多数情况下,应用都可以使用 JobScheduler 克服这些限制。
这种方式让应用安排为在未活跃运行时执行工作,
不过仍能够使系统可以在不影响用户体验的情况下安排这些作业。

关于JobScheduler的用法可以参考官方例子:android-JobScheduler

后台任务google推荐方案使用 WorkManager,
WorkManager可以自动维护后台任务,同时可适应不同的条件,
同时满足后台Service和静态广播,内部维护着JobScheduler,
而在6.0 以下系统版本则可自动切换为 AlarmManager

8)后台位置信息限制

为降低耗电量,Android 8.0(API 级别 26) 会对后台应用检索用户当前位置信息的频率进行限制。
应用每小时仅接收几次位置信息更新。

无论应用的目标 SDK 版本为何,
这些限制适用于在搭载 Android 8.0(API 级别 26) 或更高版本的设备上使用的所有应用。

前台应用(满足以下任一条件):
a:它具有可见的 Activity,无论 Activity 处于启动还是暂停状态。
b:它具有前台服务。
c:另一个前台应用已关联到该应用
(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)

解决方案:
将您的应用转至前台。

调用 startForegroundService() 以启动应用中的某个前台服务。
此前台服务处于活动状态后,会以通知的形式持续显示在通知区域。
9.0

1、新特性:
利用 Wi-Fi RTT 进行室内定位
刘海屏 API支持
通知栏功能增强
多摄像头支持和摄像头更新
HDR VP9 视频、HEIF 图像压缩和 Media API

2、non-SDK接口的使用

Android P 引入了针对非 SDK 接口的新使用限制,无论是直接使用还是通过反射或 JNI 间接使用。
无论应用是引用非 SDK 接口还是尝试使用反射或 JNI 获取其句柄,均适用这些限制。

3、挖孔屏适配

谷歌P版本提供了统一的挖孔屏方案和三方适配挖孔屏方案:
对于有状态栏的页面,不会受到挖孔屏特性的影响;
全屏显示的页面,系统挖孔屏方案会对应用界面做下移避开挖孔区显示;
已经适配的P的应用的全屏页面可以通过谷歌提供的适配方案使用挖孔区,真正做到全屏显示

P版本三方适配挖孔屏方案:
a:新增挖孔屏挖孔尺寸和位置接口

class WindowInsets {
	DisplayCutout getDisplayCutout();
}
class DisplayCutout {
	int getSafeInsetLeft();
	int getSafeInsetTop();
	int getSafeInsetRight();
	int getSafeInsetBottom();
	Region getBounds();
}

b:新窗口布局模式,允许应用程序请求是否在挖孔区域布局

class WindowManager.LayoutParams {
	int layoutInDisplayCutoutMode;
	final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
	final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
	final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
}

layoutInDisplayCutoutMode值说明:
a)LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
默认情况下,全屏窗口不会使用到挖孔区域,非全屏窗口可正常使用挖孔区域。

b)LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:
窗口声明使用挖孔区域

c)LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
窗口声明不使用挖孔区域

4、Battery Improvements 功耗解决方案
AAB(Auto Awesome Battery):
i、通过ML算法将应用进行分类,不同类型的应用功耗管控策略不一样
ii、 Firebase Cloud Messaging (FCM):管控三方消息接收的频率
iii、谷歌提供了统一的应用的管控方法:Forced App Standby (FAS),
谷歌不会通过清理应用来优化功耗

Extreme Battery Saver(EBS)谷歌超级省电模式;

Smart screen brightness:屏幕亮度调节优化算法。

影响:
谷歌功耗方案对三方应用各种管控,存在导致应用后台功能无法正常使用的可能。

5、不允许共享WebView数据目录

不能再跨进程共享单个WebView数据目录。
如果您的应用有多个使用WebView,CookieManager 或 android.webkit 包中的其他API的进程,
则当第二个进程调用WebView方法时,您的应用将崩溃。

6、移除对 Build.serial 的直接访问

需要 Build.serial 标识符的应用必须请求 READ_PHONE_STATE 权限,
然后使用 Android P 中新增的新 Build.getSerial() 函数

7、SELinux 禁止访问应用的数据目录

系统强制每个应用的 SELinux 沙盒对每个应用的私有数据目录
强制执行逐个应用的 SELinux 限制。

现在,不允许直接通过路径访问其他应用的数据目录。
应用可以继续使用进程间通信 (IPC) 机制(包括通过传递 FD)共享数据

8、常见问题:
1)java.lang.SecurityException: Failed to find provider null for user 0; …

问题原因:项目使用了ActiveAndroid,在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时,
调用 ContentResolver 的 notifyChange 方法通知数据更新,
或者调用 ContentResolver 的 registerContentObserver 方法监听数据变化时,会出现上述异常。

解决方案:
在清单文件配置

<provider
	android:name="com.activeandroid.content.ContentProvider"
	android:authorities="com.ylmf.androidclient"
	android:enabled="true"
	android:exported="false">
</provider>			

2)onError : CLEARTEXT communication to life.115.com not permitted by network security policy
问题原因:在Android P(API 28)系统的设备上或 targetSdkVersion 为28,默认要求使用加密连接。
比如,默认只能使用HTTPS进行通信,若强行使用HTTP通信,则会得到上面的异常。

解决方案:
在资源文件新建xml目录,新建文件

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
	<base-config cleartextTrafficPermitted="true" />
</network-security-config>

清单文件的application标签中添加配置:
android:networkSecurityConfig="@xml/network_security_config"

但还是建议都使用https进行传输

3)8.0,静态广播无法正常接收
问题原因: Android 8.0 引入了新的广播接收器限制,
因此您应该移除所有为隐式广播 Intent 注册的广播接收器

解决方案:
使用动态广播代替静态广播

4)Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

问题原因: Android 8.0 非全屏透明页面不允许设置方向

解决方案:android:windowIsTranslucent设置为false

二、广播在 7.0、8.0、9.0 下的适配

1、7.0广播改动

为了提升设备性能和用户体验,在Android 7.0 中删除了三项隐式广播,
以帮助优化内存使用和电量消耗

a:在 Android 7.0上 应用不会收到 CONNECTIVITY_ACTION 广播,
即使你在manifest清单文件中设置了请求接受这些事件的通知。

在前台运行的应用如果使用BroadcastReceiver 请求接收通知,
仍可以在主线程中侦听 CONNECTIVITY_CHANGE

b:在 Android 7.0上应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 类型的广播。

2、8.0广播改动

除了有限的例外情况,应用无法使用静态注册隐式广播。
它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。

三、Android 6.0 权限的适配

转载

6.0 以下的权限及在安装的时候,根据权限声明产生一个权限列表,
用户只有在同意之后才能完成app的安装。
造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)

6.0 以后运行时权限,保护了用户的隐私。

Google将权限分为两类,

一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的。
比如手机震动、访问网络等;

另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权。
比如读取sdcard、访问通讯录等。
可以通过 adb shell pm list permissions -d -g 进行查看。

如果app已被用户授权了同一组的某个危险权限,系统会立即授权,不需要用户去点击授权。
比如 app对 READ_CONTACTS 已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。

此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限
(ps:这个dialog是不能进行定制的)。

四、Android 7.0 应用共享文件(FileProvider)

对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策,
禁止在您的应用外部公开 file://URI
如果一项包含文件 URI 的 intent 离开您的应用,则抛出 FileUriExposedException 异常。

要在应用间共享文件,您应发送一项 content://URI 并授予 URI 临时访问权限。
授权的最简单方式是使用 FileProvider 类。

五、Android 7.0 共享文件的使用方式

转载

1、创建一个类继承v4包下的 FileProvider
 //不用实现任何方法
public class MyFileProvider extends FileProvider{
}
2、指定一个 FileProvider
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="com.example.myapp">
	<application>
		<provider
			android:name="com.example.myapp.fileprovider.MyFileProvider"
			//标识,建议用“${applicationId}.fileprovider”命名
			android:authorities="com.example.myapp.fileprovider" 
			android:grantUriPermissions="true"
			android:exported="false">
			<meta-data
				//文件共享目录,位于res/xml/目录下
				android:name="android.support.FILE_PROVIDER_PATHS"
				android:resource="@xml/filepaths" />
		</provider>  
	</application>
</manifest>

我们是否可以将export设置为true呢?很遗憾是不能的。
在FileProvider的内部:

@Override
public void attachInfo(Context context, ProviderInfo info) {
	super.attachInfo(context, info);
	// 抛异常
	if (info.exported) {
		throw new SecurityException("Provider must not be exported");
	}
	if (!info.grantUriPermissions) {
		throw new SecurityException("Provider must grant uri permissions");
	}
	mStrategy = getPathStrategy(context, info.authority);
}
3、指定共享路径 res/xml/filepaths.xml
<paths> 
	//path:要访问的路径
	//name:名称,生成uri时使用
	<files-path path = "images/" name = "myimages" /> 
</paths>

files-path 指的是应用程序内部存储的目录,

path:是文件的路径 .是根路径 字符串则是代表文件夹
比如:path=“aaa” 那么就代表的是该路径下的aaa文件夹

name:用于生成 Uri

比如,我们要获取该目录下名为 default_image.jpg 的文件。

生成的uri为:
content://com.example.myapp.fileprovider/myimages/default_image.jpg
图片的目录为:
/data/data/应用包名/files/images/default_image.jpg

4、使用

调用系统相册:

File imagePath = new File(Context.getFilesDir(), "images");
File requestFile = new File(imagePath, "default_image.jpg");
//构造uri
Uri fileUri = null;
   if (Build.VERSION.SDK_INT >= 24) {
       fileUri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", requestFile);
   } else {
       fileUri = Uri.fromFile(file);
   }
private Intent requestFileIntent = new Intent(Intent.ACTION_VIEW);
requestFileIntent.setType("image/jpg");
//授予权限
requestFileIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 
						   | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//保存位置
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(requestFileIntent, 0);
5、paths说明与目录对应关系

1)说明
path: 目录名称,不能为空,可以为“.”
name: 生成uri需要,不能为空

<paths xmlns:android = "http://schemas.android.com/apk/res/android"> 
	
	<!--
		<files-path> 代表 context.getFileDir() 文件保存路径为:/data/应用包名/files/
		Uri:content://com.example.myapp.fileprovider/fileDir/default_image.jpg
	-->
	<files-path
		name="fileDir" //生成uri需要
		path="."/>

	<!--
		自定义getExternalCacheDir()
		文件保存路径为:/storage/emulated/0/Android/data/应用包名/cache/update/default_image.jpg
		Uri:content://com.example.myapp.fileprovider/custompath/default_image.jpg
	-->
	<external-cache-path 
		//name生成uri需要
		//Uri:content://com.example.myapp.fileprovider/custompath/default_image.jpg
		name="custompath"
		
		//path 目录名,在 <external-cache-path>指定目录的目录下。
		//文件保存路径为:/storage/emulated/0/Android/data/应用包名/cache/update/default_image.jpg
		path="update" 	   
		/>
	<cache-path name="cache" path="."/>
	<external-path name="external" path="."/>
	<external-files-path name="external_file_path" path="."/>
	<external-path name="my_images" path="Pictures"/>
</paths>

2)对应关系

<files-path>    		代表 context.getFileDir()
<cache-path>    		代表 context.getCacheDir()
<external-path>     	代表 Environment.getExternalStorageDirectory()
<external-files-path>   代表 context.getExternalFilesDir()
<external-cache-path>   代表 getExternalCacheDir()

//这个目录,只在 API 21+ 的设备存在
<external-media-path>	代表 Context.getExternalMediaDirs()
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android、Java、C、C++、Python、Flutter、Gradle、数据结构、计算机组成原理、计算机网络、操作系统、算法、数据结构、设计模式、密码学等等 Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)实现不同操作系统上的兼容。 Java的特点包括: 面向对象:Java全面支持面向对象的特性,如封装、继承和多态,使得代码更易于维护和扩展。 安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动设备和桌面应用。 健壮性与高性能:Java通过垃圾回收机制确保内存的有效管理,同时也能通过JIT编译器优化来提升运行时性能。 标准库丰富:Java拥有庞大的类库,如Java SE(Java Standard Edition)包含基础API,用于开发通用应用程序;Java EE(Java Enterprise Edition)提供企业级服务,如Web服务、EJB等;而Java ME(Java Micro Edition)则针对小型设备和嵌入式系统。 社区活跃:Java有着全球范围内庞大的开发者社区和开源项目,持续推动技术进步和创新。 多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值