Android 13(API 33)引入了多项重要特性和行为变更,本文从 Android 开发视角,梳理核心适配点及对应的 Kotlin 实现方案,帮助开发者快速完成适配。
一、权限适配(核心变更)
Android 13 对权限体系做了精细化拆分,重点涉及媒体权限 和通知权限,需重点适配。
1. 媒体权限拆分(替代 READ_EXTERNAL_STORAGE)
Android 13 移除了 READ_EXTERNAL_STORAGE 权限,将其拆分为 3 个更细分的权限:
READ_MEDIA_IMAGES:读取图片READ_MEDIA_VIDEO:读取视频READ_MEDIA_AUDIO:读取音频
适配步骤
(1)Manifest 声明权限
xml
<!-- Android 13+ 媒体权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 兼容 Android 12 及以下 -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
(2)Kotlin 动态申请权限
kotlin
import android.Manifest
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
class MediaPermissionActivity : AppCompatActivity() {
// 权限申请回调
private val requestMediaPermissions = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val isImageGranted = permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
val isVideoGranted = permissions[Manifest.permission.READ_MEDIA_VIDEO] ?: false
val isAudioGranted = permissions[Manifest.permission.READ_MEDIA_AUDIO] ?: false
if (isImageGranted || isVideoGranted || isAudioGranted) {
// 权限申请成功,处理媒体文件
handleMediaFiles()
} else {
// 权限被拒绝,引导用户去设置页开启
showPermissionDeniedDialog()
}
}
// 申请媒体权限
private fun requestMediaPerms() {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ 申请细分权限
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.READ_MEDIA_AUDIO
)
} else {
// 兼容低版本
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
}
requestMediaPermissions.launch(permissions)
}
private fun handleMediaFiles() {
// 处理图片/视频/音频逻辑
}
private fun showPermissionDeniedDialog() {
// 权限拒绝提示弹窗
}
}
2. 通知权限(POST_NOTIFICATIONS)
Android 13 要求所有应用必须申请 POST_NOTIFICATIONS 权限才能发送通知,默认情况下权限为关闭状态。
适配步骤
(1)Manifest 声明权限
xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
(2)Kotlin 申请通知权限
kotlin
import android.Manifest
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
class NotificationPermissionActivity : AppCompatActivity() {
private val requestNotificationPermission = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
// 通知权限已授予,初始化通知
initNotification()
} else {
// 权限被拒绝,提示用户开启
showNotificationPermissionTip()
}
}
// 检查并申请通知权限
private fun checkNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (!notificationManager.areNotificationsEnabled()) {
// 申请通知权限
requestNotificationPermission.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
initNotification()
}
} else {
// 低版本无需申请,直接初始化
initNotification()
}
}
private fun initNotification() {
// 初始化通知渠道、发送通知等逻辑
}
private fun showNotificationPermissionTip() {
// 提示用户去设置页开启通知权限
}
}
二、应用内语言设置适配
Android 13 新增了应用内语言设置 API,允许应用独立于系统语言设置自身的显示语言,无需重启应用。
适配实现
kotlin
import android.os.Build
import android.os.LocaleList
import androidx.appcompat.app.AppCompatActivity
import java.util.Locale
class LanguageSettingActivity : AppCompatActivity() {
// 设置应用内语言(例如:设置为简体中文)
private fun setAppLanguage(locale: Locale) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ 官方 API
val appLocale = android.app.LocaleManager.getApplicationLocales(baseContext)
appLocale.setLocaleList(LocaleList(locale))
android.app.LocaleManager.setApplicationLocales(baseContext, appLocale)
// 刷新界面(无需重启 Activity)
recreate()
} else {
// 兼容低版本(传统方式)
val resources = resources
val configuration = resources.configuration
configuration.setLocale(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
recreate()
}
}
// 示例:设置为英文
fun setEnglish() {
setAppLanguage(Locale.ENGLISH)
}
// 示例:设置为简体中文
fun setChinese() {
setAppLanguage(Locale.SIMPLIFIED_CHINESE)
}
// 获取当前应用内语言
private fun getCurrentAppLocale(): Locale {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val locales = android.app.LocaleManager.getApplicationLocales(baseContext)
locales[0] ?: Locale.getDefault()
} else {
resources.configuration.locale
}
}
}
三、照片选择器适配
Android 13 强化了照片选择器(Photo Picker),提供更安全的媒体选择方式,无需申请存储权限即可让用户选择图片/视频。
适配实现
kotlin
import android.content.Intent
import android.os.Build
import android.provider.MediaStore
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
class PhotoPickerActivity : AppCompatActivity() {
// 照片选择器回调
private val pickImageLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val uri = result.data?.data
uri?.let {
// 处理选中的图片 URI
handleSelectedImage(it)
}
}
}
// 打开照片选择器
private fun openPhotoPicker() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ 照片选择器(支持多选、指定媒体类型)
val intent = Intent(MediaStore.ACTION_PICK_IMAGES).apply {
// 设置最多选择数量
putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 5)
// 指定媒体类型:仅图片(可选:VIDEO、IMAGES_AND_VIDEO)
type = "image/*"
}
pickImageLauncher.launch(intent)
} else {
// 兼容低版本(传统相册选择)
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
pickImageLauncher.launch(intent)
}
}
private fun handleSelectedImage(uri: android.net.Uri) {
// 加载图片、上传等逻辑
}
}
四、其他关键适配点
1. 前台服务类型限制
Android 13 要求前台服务必须指定具体的 foregroundServiceType,且新增了 health、remoteMessaging 等类型。
Manifest 声明示例
xml
<service
android:name=".MyForegroundService"
android:foregroundServiceType="location|mediaPlayback" />
Kotlin 启动前台服务
kotlin
import android.app.Notification
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
class MyForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification() // 自定义创建通知
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// 指定前台服务类型
startForeground(1, notification, Service.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
} else {
startForeground(1, notification)
}
return START_STICKY
}
private fun createNotification(): Notification {
// 构建前台服务通知(需创建通知渠道)
return Notification.Builder(this, "media_channel")
.setContentTitle("播放中")
.setSmallIcon(R.drawable.ic_play)
.build()
}
override fun onBind(intent: Intent?): IBinder? = null
}
2. 剪贴板访问限制
Android 13 限制应用在后台访问剪贴板,仅当应用处于前台且用户主动触发(如点击按钮)时才能访问。
kotlin
// 仅在前台主动触发时访问剪贴板
fun getClipboardContent() {
val clipboard = getSystemService(CLIPBOARD_SERVICE) as android.content.ClipboardManager
val clipData = clipboard.primaryClip
if (clipData != null && clipData.itemCount > 0) {
val content = clipData.getItemAt(0).text.toString()
// 处理剪贴板内容
}
}
3. 蓝牙权限变更
Android 13 将蓝牙权限拆分为 BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT,需按需申请:
xml
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
五、适配注意事项
- 编译版本升级 :将
compileSdkVersion和targetSdkVersion升级到 33 及以上,才能使用 Android 13 新 API。 - 兼容性测试:适配后需在 Android 13 设备和低版本设备(如 Android 12、11)上充分测试,避免兼容性问题。
- 权限申请时机:遵循"按需申请"原则,避免启动页一次性申请所有权限,提升用户体验。
- 隐私合规:适配媒体权限、剪贴板等变更时,需符合应用商店的隐私政策要求。
六、参考资源
总结
Android 13 的适配核心集中在权限精细化 、用户隐私保护 和体验优化(如应用内语言、照片选择器)。通过上述 Kotlin 代码示例,可快速完成核心特性的适配,同时需注意低版本兼容,确保应用在全版本设备上稳定运行。