Android Q
前言
感觉还没怎么见过Android 9.0,10 都已经出来了,虽然只是测试版,但是提前了解下也是很有必要的,尤其对于我们开发者而言。新版本出来意味着适配工作又要搞起了,之前的华为开发者平台已经要求上架的应用目标版本适配到Q
https://developer.huawei.com/consumer/cn/notice/20190328
下面主要介绍下Q的适配。
官网:https://developer.android.google.cn/
正文
首先是下载Q所需要的工具及模拟器,我用AS看了一下,需要4.5G,算了,你们搞吧。本文完。
需要注意的是,如果要编译Q,需要在应用的bulid.gradle中声明:
- compileSdkVersion “android-Q”
- buildToolsVersion “29.0.0 rc1”
- targetSdkVersion “Q”
官方提到的重大隐私变更包括一下几点:
- SCOPED STORAGE
简单的说Q又修改了存储文件权限,现在Q上的每个应用都有自己一个独立的沙盒(存储区域),这个区域只有自己的应用可以访问,这个沙盒里面的文件随着应用被卸载而删除.
我们可以直接访问而 不 再 需 要 申 请 \color{red}{不再需要申请} 不再需要申请READ_EXTERNAL_STRAGE和WRITE权限。
官方推荐我们在外部存储文件的位置是Context.getExternalFilesDir(String type),type可以传Environment.DIRECTORY_IMAGES等类型,如果应用中没有这个文件夹,会自动创建,路径为/storage/emulated/0/Android/data/包名/files/Music
,我们可以根据传入不同的type类型保存不同类型的文件。
如果我们希望应用卸载的时候也保存这些文件,那么最好保存到公共文件夹下,如公共的照片,音乐,下载等。
如果我们希望访问公共文件夹照片,视频,音乐中其他应用的文件,需要READ_MEDIA_IMAGES ,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO 权限,具体根据文件类型定。
如果希望访问公共文件夹下载中其他应用的文件,则必须通过系统的文件选择器来选择。通过ContentResolver.openFileDescriptor()之类的API来读取数据。
- DEVICE LOCATION
Q限制了应用处于后台时获取位置信息的能力,这项行为在Q上是默认开启的。
我们现在如果在Q上请求获取位置信息,会弹出选项:
在使用中(仅限前台)
始终(前台和后台)
Q中新增了ACCESS_BACKGROUND_LOCATION权限,这个权限只是用来在后台运行时访问,除非应用的某个 Activity 可见或应用正在运行前台服务,否则应用将被视为在后台运行。
<manifest>
<!--允许获得精确的GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!--允许获得粗略的基站网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!--允许后台获取位置-->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
因此,要获取后台位置信息需要以下操作:
开启前台服务。类型为
l
o
c
a
t
i
o
n
\color{red}{location}
location
<service
android:name="MyNavigationService"
android:foregroundServiceType="location" ... >
...
</service>
我们在获取位置信息之前需要判断是否已经开启了权限:
val permissionAccessCoarseLocationApproved = ActivityCompat
.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) ==
PackageManager.PERMISSION_GRANTED
if (permissionAccessCoarseLocationApproved) {
val backgroundLocationPermissionApproved = ActivityCompat
.checkSelfPermission(this, permission.ACCESS_BACKGROUND_LOCATION) ==
PackageManager.PERMISSION_GRANTED
if (backgroundLocationPermissionApproved) {
//前后台都可以获取位置信息,因此不需要开启前台服务
// App can access location both in the foreground and in the background.
// Start your service that doesn't have a foreground service type
// defined.
} else {
//只能获取前台位置,展示对话框告诉用户必须始终获取位置信息才能正常工作,然后请求后台位置信息
// App can only access location in the foreground. Display a dialog
// warning the user that your app must have all-the-time access to
// location in order to function properly. Then, request background
// location.
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
your-permission-request-code
)
}
} else {
//没有任何位置信息权限,申请一下
// App doesn't have access to the user's location at all. Make full request
// for permission.
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION),
your-permission-request-code
)
}
- BACKGROUND APP STARTS
Q针对从后台启动Activity做了限制,如果在没有和用户互动情况下启动Activity,则很可能会影响您的应用,比如一般App都用2s的启动页,如果在2s内切换到后台,此时就可认为是后台启动Activity,目前仅仅会toast提示,这项行为变更是只要安装在Q设备上都会生效。
This background activity start from package-name will be blocked in future Q builds.
目前限制为只有满足以下一个或多个条件才可以启动Activity:
a.该应用具有可见窗口,例如在前台运行的 Activity。
b.在前台运行的另一个应用会发送属于该应用的 PendingIntent。
c.系统发送属于该应用的 PendingIntent
d.系统向应用发送广播
因此,为了避免这种情况发生,官方推荐我们通过创建通知提示用户,而不是直接打开Activity,当然,某些功能可能确实需要,比如来电,解决办法如下:
-> 创建高优先级通知,使用全屏PendingIntent.如果使用全屏Intent,则
需
要
申
请
U
S
E
F
U
L
L
S
C
R
E
E
N
I
N
T
E
N
T
权
限
\color{red}{需要申请USE_FULL_SCREEN_INTENT权限}
需要申请USEFULLSCREENINTENT权限
->通过
开
启
前
台
服
务
\color{red}{开启前台服务}
开启前台服务与通知相关联
该功能默认是关闭的,如果要开启,需要到设置里面修改。
val fullScreenIntent = Intent(this, CallActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationBuilder = 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 Q, you
// need to request the USE_FULL_SCREEN_INTENT permission in order for the
// platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true)
val incomingCallNotification = notificationBuilder.build()
注意:应用无法通过前台服务让应用处于前台豁免该管控
4. HARDWARE IDENTIFIES
这项变更主要是对系统标识增加的几项限制,有助于保护用户的隐私。
只要运行在Q上的应用:
获取随机的MAC地址,通过调用getRandomizedMacAddress()获取特定网络的随机MAC地址。
获取系统的MAC地址,通过调用getWifiMacAddress()检索实际硬件的MAC地址。
如果需要访问设备网络状态,使用NetworkStatsManager 和 ConnectivityManager 类。
获取IMEI和设备序列号:Q需要READ_PRIVILEGED_PHONE_STATE权限,低版本需要READ_PHONE_STATE,否则发生异常。
5. LOCATION AND NETWORK
这就用的不多了,但是依然是在Q 上运行的所有应用会产生影响。如:
Q 上运行的应用无法启用或停用 WLAN,需要到设置面板。
以Q为目标平台影响:
应用无法使用 WLAN API、WLAN Aware API 或 Bluetooth API 中的多种方法,除非应用具有 ACCESS_FINE_LOCATION 权限。
折叠屏
除此之外,Q一个极为重要的变更就是支持可折叠屏,怕了怕了,这个适配对我们开发者而言感觉是相当困难啊。为了更好的适配可折叠屏,官网建议:
- 为了保证屏幕的展开和收缩时应用的连续性,我们应该及时的保存当前的状态和位置。
- 如果应用屏幕可支持大小调整,需要增加Activity属性
resizeableActivity=true
,这个好像在8.0就有了,如果设置false,则代表应用不支持屏幕大小调整,但是展开的情况下系统会调整到展开的合适位置。 - 这是目前Q的屏幕比例,如果不支持某些比率,可以通过 maxAspectRatio和minAspectRatio来指示应用可以处理的最高和最低比率,屏幕超出这些限制,则可能会处于兼容模式。
- 在Q中,新增了 onTopResumedActivityChanged() 方法,可以监听当前Activity获取或者是失去topmost resumed position(最顶部显示的位置),我们可以在这个方法里对一些公用的资源进行处理。
protected void onTopResumedActivityChanged(boolean topResumed) {
if (topResumed) {
// Top resumed activity
// Can be a signal to re-acquire exclusive resources
} else {
// No longer the top resumed activity
}
}