问题描述
在我们的交互设计中,当应用退到后台时,会显示一个小的悬浮窗,点击这个悬浮窗就跳回应用。
跳回应用的代码实现:
private fun backToApp() {
val intent = Intent(this, MainActivity::class.java)
try {
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
pendingIntent.send()
} catch (e: Exception) {
e.printStackTrace()
RNLog.e(e.toString())
}
}
- 注意,这里一定要用PendingIntent,不能直接startActivity,不然回到应用的速度非常慢,在1s以上。
讲道理,这个代码没有任何问题,然后测试报了个bug说,小米MIX3机型上,点击悬浮窗没有反应。
问题分析
打日志,看到点击悬浮窗后,logcat有一条报错日志:
ExtraActivityManagerService: MIUILOG- Permission Denied Activity
上网一搜,发现这是MIUI自己搞事情:
所以,想要能正常跳回应用,还需要开启一个权限:
问题解决
解决这个问题,思路是明确的:
- 判断是MIUI系统
- 代码实现检测“后台弹出界面”是否已授权
- 跳转应用权限设置,让用户设置“后台弹出界面”权限
网上有一些文章写了这个问题怎么处理了,但是讲得都不那么全,有的还有一些奇奇怪怪的处理方法。我把这些东西大致看了一下,大胆假设,小心求证,去伪存真,总结出了正确答案。
// 判断是MIUI系统
private fun isMIUI(): Boolean {
val device = Build.MANUFACTURER
if (device == "Xiaomi") {
val code = SystemPropertiesProxy.get("ro.miui.ui.version.code", "")
val version = SystemPropertiesProxy.get("ro.miui.ui.version.name", "")
Logger.d("The device is MIUI $code, version: $version" )
if (code.isNotEmpty() || version.isNotEmpty()) {
return true
}
}
return false
}
// SystemPropertiesProxy这个是对SystemProperties hidden API的反射封装,GitHub上一搜一大把
// 代码实现检测“后台弹出界面”是否已授权
// op: 10021 这个是某位大哥在评论里回复的,测试可用
private fun checkPermissionInternal(): Boolean {
val manager = mReactContext.getSystemService(APP_OPS_SERVICE) as AppOpsManager
return try {
val clazz = AppOpsManager::class.java
val method = clazz.getDeclaredMethod("checkOp", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java)
val op = 10021
AppOpsManager.MODE_ALLOWED == method.invoke(manager, op, getCallingUid(), mReactContext.packageName) as Int
} catch (e: Exception) {
Logger.e(getStackTraceString(e))
false
}
}
// 跳转应用权限设置,让用户设置“后台弹出界面”权限
// 这个在小米官方dev文档里有写
fun requestBackPermission() {
val intent = Intent()
intent.action = "miui.intent.action.APP_PERM_EDITOR"
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.putExtra("extra_pkgname", mReactContext.packageName)
currentActivity?.let {
it.startActivity(intent)
}
}