好久不见~ 最近几个月改变挺大的,不论是自己的家庭仍是社会环境,把我们能做的做好,渐渐适应新的改变,这也是一种不行或缺的才能吧!
Android14 行将正式发布,作为开发者需求留意哪些内容?长话短说,一起来看看吧~
主要分为两部分:
一是影响一切的 Android 运用,这些改动会影响一切的 App,只需你的 App 装置在了 Android14 的设备上,都会遭到这些影响;
二是当 targetSdkVersion 升级到 34 后,我们的 App 所遭到的影响。这一篇先来说说第一部分的内容,即现有 App 装置到 Android14 手机上,会有哪些影响。
1. SCHEDULE_EXACT_ALARM 权限默许封闭
这个权限的全称是 android.permission.SCHEDULE_EXACT_ALARM
,用于是否敞开设置准确闹钟的权限。准确的闹钟适用于用户指守时刻的告诉,或是在切当的时刻需求履行的操作。
假如 App 的 targetSdkVersion 设置的是 33(Android13)或更高,在 Android14 的设备上运转时,这个权限便是默许封闭的。所以,当 App 中有用到准确闹钟,需求在切当的时刻点去做操作,那么就需求在 Manifest 文件中显式地请求这个权限并需求在运用时动态向用户获取该权限。
具体地说便是,当运用 AlarmManager
中的
setExact(int type, long triggerAtMillis, PendingIntent operation)
、
setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)
、
setAlarmClock(AlarmManager.AlarmClockInfo info, PendingIntent operation)
这三个函数时,假如 targetSdkVersion >= 33,且在 Android14 设备上没有显式请求该权限,则会抛出一个 SecurityException
反常。
特殊状况:
1)假如用户经过“备份与恢复”功能将 App 传输到一个 Android14 的设备上,则此 App 的该权限默许仍是封闭的;
2)假如一个 App 现已敞开了该权限,当设备升级到 Android14 后,此 App 的该权限是敞开的状况;
3)当准确闹钟是经过 OnAlarmListener
设置的,则无需请求该权限。例如:setExact(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)
这个办法就无需请求。
用的比较多的 API:
1)boolean canScheduleExactAlarms()
判别是否能够设置准确闹钟(API >= 31 才有此判别办法);
2)AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED
播送音讯常用来监听用户敞开或封闭该权限的回调(API >= 31 才有此播送音讯类型)。
不主张的运用场景:
1)假如 App 在生命周期内组织重复性的操作,能够运用 Handler
中的 postAtTime
等来代替。相反,假如是要设置 30min 后或许明天下午 2 点的操作,则主张运用;
2)组织在后台进行的一些操作,例如:下载更新App或许上传日志等。主张运用 WorkManager
而不是准确闹钟;
3)当体系处于空闲时,在大约的时刻点处理业务,则能够调用非准确闹钟的一些 API 处理,例如运用 setAndAllowWhileIdle()
而不是 setExactAndAllowWhileIdle()
办法;
4)用户指定的在大约特守时刻点产生的,或许在一个时刻窗口内产生的业务;
适配流程:
1)调用 alarmManager.canScheduleExactAlarms()
检查是否有该权限;
2)假如没有权限,则需求经过 Intent
,设置 Action
为 ACTION_REQUEST_SCHEDULE_EXACT_ALARM
并加上运用包名调起设置页面,让用户赋予权限,回来后在 onResume
回调中判别是否权限是否已请求。
下面是一个例子:
// code 1
// MyFragment.kt 中的代码
private val ALARM_REQUEST_CODE = 123
private var getExactSchedulePermission = false
@RequiresApi(Build.VERSION_CODES.S)
private fun scheduleAlarm() {
// 创立一个 Intent,用于指定守时任务触发时要履行的操作
val intent = Intent(requireContext(), AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
requireContext(),
ALARM_REQUEST_CODE,
intent,
PendingIntent.FLAG_IMMUTABLE
)
// 获取 AlarmManager 实例
val alarmManager = requireActivity().getSystemService(Context.ALARM_SERVICE) as AlarmManager
// 触发时刻(这儿运用相对时刻)
val triggerTime = SystemClock.elapsedRealtime() + 5000 // 5秒后触发
// 设置守时任务
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent)
} else {
// 假如没有权限则翻开设置页,让用户颁发该 App 的准确闹钟权限
startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM))
getExactSchedulePermission = true
}
}
@RequiresApi(Build.VERSION_CODES.S)
override fun onResume() {
super.onResume()
if (getExactSchedulePermission) {
scheduleAlarm()
getExactSchedulePermission = false
}
}
// AlarmReceiver.kt
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "Alarm triggered!", Toast.LENGTH_SHORT).show()
}
}
当运转 scheduleAlarm() 办法后,过 5 秒就会有 Toast 出现~
日历或闹钟运用需求在运用停止运转时发送日历提示、唤醒闹钟或提示。这些运用能够请求 USE_EXACT_ALARM
惯例权限。体系将在装置时颁发 USE_EXACT_ALARM
权限,拥有此权限的运用将能够像具有 SCHEDULE_EXACT_ALARM
权限的运用相同设置准确闹钟。
小结:能不必就不必。假如之前已用到准确闹钟,则需求新增权限获取逻辑。
2. 动态播送当 App 进入缓存态时将会入队保存
在 Android14 中,我们运用 Context
上下文注册的动态播送接纳器,能够在 App 进入缓存状况时,将已发送还未接纳的播送放入到一个行列中保存。当 App 离开缓存状况(比方进入前台),则体系会传递一切已加入行列的播送。某些播送的多个实例能够合并为一个播送。
而在 Manifest 文件中注册的静态播送接纳器,则不能进入行列,它们会在 App 从缓存状况中被移除销毁时,进行播送传递。
什么是缓存状况下的 App?简单了解便是在后台的 App,现在不在前台的进程,因而,假如体系其他地方需求内存,体系能够依据需求自由地停止这些进程。当然停止的次序是最老未运用的最早被停止。
3. App 只能停止自己的后台进程
从 Android14 开端,调用 killBackgroundProcesses()
时,只能停止自己运用的后台进程。假如传入另一个运用的软件包称号,此办法对该运用的后台进程没有影响,而且 Logcat 中会显现以下音讯:
Invalid packageName: com.example.anotherapp
官方给出的解说是:
您的运用不该运用 killBackgroundProcesses() API,也不得以其他方法尝试影响其他运用的进程生命周期,即便在旧版操作体系上也是如此。Android 旨在让缓存运用在后台运转,并在体系需求内存时主动停止它们。假如您的运用不必要地停止其他运用,则由于之后需求彻底重启这些运用,因而或许会下降体系功能并添加耗电量,这比恢复现有缓存运用所耗费的资源要多得多。
该 API 是 ActivityManager
供给的,完好的办法声明:
// code 2
public void killBackgroundProcesses (String packageName)
此外,运用它还需求在 Manifest 文件中请求权限 Manifest.permission.KILL_BACKGROUND_PROCESSES
.
经测验,我发现这个 API 有点奇怪:被杀死的后台进程马上又会重启,额。。。这是什么操作??
测验代码比较简单,便是在别的一个进程中敞开一个 Service
,然后调用 killBackgroundProcesses
办法即可,依据打印的 Service
生命周期可看出,该 Service
的确先被杀死然后又走了一次 onCreate
、onStartCommand
生命周期,代码和成果如下所示:
// code 3
// Manifest 文件声明 Service 在另一个进程中启动
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<service
android:name=".MyService"
android:process="com.secondProcess" />
// 启动 Service
startService(Intent(requireContext(), MyService::class.java))
// 杀死后台进程
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.killBackgroundProcesses(context.packageName)
log 打印成果:
从图上可知,在 Android14 的设备上,调用 killBackgroundProcesses
办法能够杀死自己 App 的后台进程,但会当即重新启动。在源码中也找到了下面的代码,尽管已被抛弃:
// code 3
@Deprecated
public void restartPackage(String packageName) {
killBackgroundProcesses(packageName);
}
看来这个 API 便是用来重启 App 的后台进程的?
试了下在 Android14 设备上的 A App 中调用此 API 去杀死 B App 的后台进程,的确没有任何作用;但假如是在 Android14 以下的设备上调用,的确能够杀死 B App 的后台进程。感兴趣的同学也可试一试。
小结:killBackgroundProcesses
API 并没有什么卵用~(欢迎大佬指点)
4. 安全方面
在 Android14 体系手机上,将无法装置 targetSdkVersion < 23(低于Android6.0)的 App。
媒体包称号在 Android14 上或许会被躲藏。现在媒体库支撑依照 OWNER_PACKAGE_NAME
列查询某包名下的一切媒体文件,一个运用存储的媒体文件是带有它自己的包名信息的。这些信息将在 Android14上被躲藏,除非满意以下条件之一:
1)存储媒体文件的运用包称号一直对其他运用可见(自己开放给一切其他 App);
2)查询媒体库的运用获得了 QUERY_ALL_PACKAGES
权限(其他 App 向用户请求获得了权限)。
举个栗子:
当一个运用存储了一个媒体文件(例如一张相片或一个视频),它会在媒体库中记载该文件的信息,包括该文件的一切者包名。其他运用能够查询媒体库以获取这些信息,以便在自己的运用中显现该文件或与之交互。
在 Android14 及以后的版别中,假如存储媒体文件的运用的包名不是一直对其他运用程序可见的,则在查询媒体库时,一切者包名将被躲藏或替换为匿名值。例如,假如一个运用包名为“com.example.app”,它存储了一个媒体文件,但它的包名被躲藏了,那么在查询媒体库时,一切者包名或许会被替换为“com.android.providers.media”。
但是,假如存储媒体文件的运用具有一直对其他运用可见的包名,或许查询媒体库的运用程序具有QUERY_ALL_PACKAGES
权限,则能够看到媒体库中的完好一切者包名。例如,一个运用名为“com.example.app”,它存储了一个媒体文件,而且它的包名一直对其他运用程序可见,那么在查询媒体库时,一切者包名将显现为“com.example.app”。
5. 用户体会方面
5.1 可独自对相片和视频拜访权限进行授权
假如你的 App 以 Android13 或更高版别为方针平台(即 targetSdkVersion >= 33),且在 Android14 的设备上运转时,用户能够颁发对其相片和视频的部分拜访权限,即独自设置 READ_MEDIA_IMAGES
或 READ_MEDIA_VIDEO
。
即请求 READ_MEDIA_IMAGES
权限时,仅会显现手机上一切图片给用户进行挑选;请求 READ_MEDIA_VIDEO
权限时,仅会显现手机上一切的视频给用户进行挑选。用户能够更加细致地挑选将哪些相片或视频授权给 App 读取运用。
新的体系对话框长这样:
1)挑选相片和视频: Android14 中的新功能。用户挑选希望供给给运用的具体相片和视频。
2)全部答应:用户颁发对设备上的一切相片和视频的完好拜访权限。
3)不答应:用户回绝颁发一切拜访权限。
留意:
1)当运用现已在运用体系的 相片挑选器,则无需履行任何操作即可支撑此改变;
2)READ_MEDIA_IMAGES
和 READ_MEDIA_VIDEO
仅在 Android13 或以上的版别才能运用;
新增了一个 READ_MEDIA_VISUAL_USER_SELECTED
权限,归于 Dangerous 等级。用于在用户点击自定义的相片挑选器需求请求拜访相片和视频的权限时运用,这样就不必去请求 READ_MEDIA_IMAGES
和 READ_MEDIA_VIDEO
这两个权限了。
小结:开发者不必管,新的权限很鸡肋,暂时用不上,之前读取相片和视频的相关逻辑也不必改。
5.2 更安全的全屏告诉展现
在 Android11(API level 30)上就能够调用 Notification.Builder.setFullScreenIntent
办法在锁屏上展现一些全屏的告诉了,不过得在 Manifest 文件中请求 USE_FULL_SCREEN_INTENT
权限。
全屏告诉是为了让用户当即留意到的高优先级告诉而规划的,例如来电或用户装备的闹钟,在展现全全屏告诉时,用户只能上滑退出,如下图所示的体系提示。
从 Android14 开端,答应运用此权限的运用程序仅限于那些只供给通话和警报的运用。关于其他运用,Google Play 商铺会吊销它们默许的 USE_FULL_SCREEN_INTENT
权限。
能够运用新的 API NotificationManager.canUseFullScreenIntent()
检查运用是否有权限;假如没有,能够用新的 ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
来启动用户能够颁发该权限的设置页面。
奇怪的是我在 Android14 官方的虚拟机上并没有翻开告诉成功,更不必说翻开全屏告诉了。不过的确能够翻开设置全屏告诉权限开关的页面,如下是全屏告诉权限设置图及主要相关代码:
// code 4
val notificationBuilder = NotificationCompat.Builder(requireContext())
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
.setContentTitle("Notification Title")
.setContentText("Notification text")
// 创立一个PendingIntent,点击Notification时翻开指定页面
val intent = Intent(context, NotificationFullActivity::class.java)
val fullScreenIntent =
PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE)
notificationBuilder.setFullScreenIntent(fullScreenIntent, true) // 翻开全屏告诉
// notificationBuilder.setContentIntent(fullScreenIntent) // 翻开普通页面
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Android 8.0 Oreo以上需求设置告诉渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "your_channel_id"
val channelName: CharSequence = "Your Channel Name"
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(channelId, channelName, importance)
channel.description = "Channel description"
notificationManager.createNotificationChannel(channel)
notificationBuilder.setChannelId(channelId)
}
notificationManager.notify(5, notificationBuilder.build())
if (notificationManager.canUseFullScreenIntent()) {
notificationManager.notify(5, notificationBuilder.build())
} else {
// 翻开设置页
val intent = Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT)
intent.data = Uri.fromParts("package", requireActivity().packageName, null)
startActivity(intent)
}
假如哪位大佬在 Android14 设备上成功地翻开了全屏告诉,麻烦交流一下,谢谢。
小结:大部分 App 用不上这个全屏告诉功能,个人觉得并不是很重要。。。
5.3 关于不行封闭告诉用户体会方法的改变
假如运用向用户显现不行封闭的前台告诉的话需求留意:Android14 中答使用户封闭此类告诉。即之前经过 Notification.Builder#setOngoing(true)
或 NotificationCompat.Builder#setOngoing(true)
设置 Notification.FLAG_ONGOING_EVENT
来阻挠用户封闭前台告诉的运用要当心了。FLAG_ONGOING_EVENT
的行为已产生改变,用户在 Android14 上能够封闭此类告诉。
以下状况,此类告诉仍不行封闭:
1)当手机处于锁定状况时;
2)假如用户挑选全部铲除告诉操作(有助于避免意外封闭);
此外,下列的几种状况并没有改变:
1)运用 CallStyle 创立的告诉,即来电告诉的样式;
2)设备策略控制器(DPC)和针对企业的支撑包;
小结:Android 的告诉管理只会越来越严厉,早就应该管管了。其实就算 Android14 手机上没有这个功能,现在绝大多数手机厂商现已都能够禁止 App 弹出告诉了,所以这个也没啥。。。
以上便是本篇的一切内容,主要依据官方文档自己实践操作了一番,能够看出,现有的 App 假如直接装置到 Android14 的手机上,并不会有太多的问题,许多东西其实并不必别的处理,当然主张仍是依据本篇内容查漏补缺比较好。假如还想了解 targetSdkVersion 升级到 34(Android14)还需求留意哪些内容,欢迎重视我,我们下篇见!
更多内容,欢迎重视大众号:修之竹 或许查看 修之竹的 Android 专辑
赞人玫瑰,手留余香!欢迎点赞、转发~ 转发请注明出处~
参考文献
- Android 14 官方文档 https://developer.android.com/about/versions/14
- developer.android.google.cn/about/versi…
- developer.android.google.cn/about/versi…
- developer.android.google.cn/guide/compo…
- developer.android.google.cn/about/versi…
- Android 14 快速适配要点; 恋猫de小郭; https:///post/7231835495557890106?searchId=202307240025039D8229C74EA62159077B