Android 后台启动Activity适配

这篇具有很好参考价值的文章主要介绍了Android 后台启动Activity适配。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在Android 9及以下版本,后台启动Activity相对自由,但是如果在Activity上下文之外启动Activity会有限制。

Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag

所以此时需要给intent添加flag:FLAG_ACTIVITY_NEW_TASK。

在Android版本10及以后版本, 引入了后台执行限制,限制了应用在后台执行操作的能力。非核心任务的后台启动 Activity 可能会受到限制。详情可参见官方文档:从后台启动 Activity 的限制。

根据文档可知,大致有两种方案可实现从后台启动Activity。

方案一:设置全屏Notification

设置Notification时通过setFullScreenIntent添加一个全屏Intent对象,可以在Android 10上从后台启动一个Activity界面,需要在Manifest.xml清单文件中加上:

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

示例代码如下:

private fun getChannelNotificationQ(context: Context, title: String?, content: String?): Notification {
        val fullScreenPendingIntent = PendingIntent.getActivity(
            context,
            0,
            DemoActivity.genIntent(context),
            PendingIntent.FLAG_UPDATE_CURRENT
        )
        val notificationBuilder = NotificationCompat.Builder(context, ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(title)
            .setContentText(content)
            .setPriority(NotificationCompat.PRIORITY_MAX)
            .setCategory(Notification.CATEGORY_CALL)
            .setOngoing(true)
            .setFullScreenIntent(fullScreenPendingIntent, true)
        return notificationBuilder.build()
    }
方案二:获取SYSTEM_ALERT_WINDOW权限

如果用户已向应用授予SYSTEM_ALERT_WINDOW权限,则可以在后台启动Activity。在 Android 10 Go 版本中,应用已经无法直接获得SYSTEM_ALERT_WINDOW权限。不过Android引入了一种称为"Display over other apps"(在其他应用上层显示)的新权限体系。这种新的权限体系允许应用请求"TYPE_APPLICATION_OVERLAY"类型的窗口权限。申请步骤如下:

在Manifest.xml清单文件中加上:

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

代码中发起请求权限申请:

if (!Settings.canDrawOverlays(this)) {
     Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
     intent.setData(Uri.parse("package:" + getPackageName()));
     startActivityForResult(intent, 0);
}
定制化ROM新权限

有些机型增加了一项权限——后台弹出界面,比如在华为、 小米等设备上便新增了这项权限,且默认是关闭的,除非加入了它们的白名单。而且如果权限是关闭的,那么前面所说的两种方案将无效。所以在这些机型上,必须获取后台弹出界面权限,才能够从后台启动Activity。

判断是否获取弹出界面权限:

object PopBackgroundPermissionUtil {
    private const val TAG = "PopPermissionUtil"

    private const val HW_OP_CODE_POPUP_BACKGROUND_WINDOW = 100000
    private const val XM_OP_CODE_POPUP_BACKGROUND_WINDOW = 10021

    /**
     * 是否有后台弹出页面权限
     */
    fun hasPopupBackgroundPermission(): Boolean {
        if (isHuawei()) {
            return checkHwPermission()
        }
        if (isXiaoMi()) {
            return checkXmPermission()
        }
        if (isVivo()) {
            checkVivoPermission()
        }
        if (isOppo() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            return Settings.canDrawOverlays(sContext)
        }
        return true
    }

    fun isHuawei(): Boolean {
        return checkManufacturer("huawei")
    }

    fun isXiaoMi(): Boolean {
        return checkManufacturer("xiaomi")
    }

    fun isOppo(): Boolean {
        return checkManufacturer("oppo")
    }

    fun isVivo(): Boolean {
        return checkManufacturer("vivo")
    }

    private fun checkManufacturer(manufacturer: String): Boolean {
        return manufacturer.equals(Build.MANUFACTURER, true)
    }

    private fun checkHwPermission(): Boolean {
        val context = sContext
        try {
            val c = Class.forName("com.huawei.android.app.AppOpsManagerEx")
            val m = c.getDeclaredMethod(
                "checkHwOpNoThrow",
                AppOpsManager::class.java,
                Int::class.javaPrimitiveType,
                Int::class.javaPrimitiveType,
                String::class.java
            )
            val result = m.invoke(
                c.newInstance(),
                *arrayOf(
                    context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager,
                    HW_OP_CODE_POPUP_BACKGROUND_WINDOW,
                    Binder.getCallingUid(),
                    context.packageName
                )
            ) as Int
            Log.d(
                TAG,
                "PopBackgroundPermissionUtil checkHwPermission result:" + (AppOpsManager.MODE_ALLOWED == result)
            )
            return AppOpsManager.MODE_ALLOWED == result
        } catch (e: Exception) {
            //ignore
        }
        return false
    }

    private fun checkXmPermission(): Boolean {
        val context = sContext
        val ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
        try {
            val method = ops.javaClass.getMethod(
                "checkOpNoThrow", *arrayOf<Class<*>?>(
                    Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java
                )
            )
            val result = method.invoke(
                ops,
                XM_OP_CODE_POPUP_BACKGROUND_WINDOW,
                Process.myUid(),
                context.packageName
            ) as Int
            Log.d(
                TAG,
                "PopBackgroundPermissionUtil checkXmPermission result:" + (AppOpsManager.MODE_ALLOWED == result)
            )
            return result == AppOpsManager.MODE_ALLOWED
        } catch (e: Exception) {
            //ignore
        }
        return false
    }

    private fun checkVivoPermission(): Boolean {
        val context = sContext
        val uri =
            Uri.parse("content://com.vivo.permissionmanager.provider.permission/start_bg_activity")
        val selection = "pkgname = ?"
        val selectionArgs = arrayOf(context.packageName)
        var result = 1
        val contentResolver = context.contentResolver
        try {
            contentResolver.query(uri, null, selection, selectionArgs, null).use { cursor ->
                if (cursor!!.moveToFirst()) {
                    result = cursor.getInt(cursor.getColumnIndex("currentstate"))
                }
            }
        } catch (exception: Exception) {
            //ignore
        }
        Log.d(
            TAG,
            "PopBackgroundPermissionUtil checkVivoPermission result:" + (AppOpsManager.MODE_ALLOWED == result)
        )
        return result == AppOpsManager.MODE_ALLOWED
    }

}

跳转弹出界面权限界面:

class SystemAlertWindow(private val mSource: Activity) {
    
    fun start(requestCode: Int) {
        var intent: Intent?
        intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (MARK.contains("meizu")) {
                meiZuApi(mSource)
            } else {
                MdefaultApi(mSource)
            }
        } else {
            if (MARK.contains("huawei")) {
                huaweiApi(mSource)
            } else if (MARK.contains("xiaomi")) {
                xiaomiApi(mSource)
            } else if (MARK.contains("oppo")) {
                oppoApi(mSource)
            } else if (MARK.contains("vivo")) {
                vivoApi(mSource)
            } else if (MARK.contains("meizu")) {
                meizuApi(mSource)
            } else {
                LdefaultApi(mSource)
            }
        }
        try {
            mSource.startActivityForResult(intent, requestCode)
        } catch (e: Exception) {
            intent = appDetailsApi(mSource)
            mSource.startActivityForResult(intent, requestCode)
        }
    }

    private fun huaweiApi(context: Context): Intent? {
        val intent = Intent()
        intent.setClassName(
            "com.huawei.systemmanager",
            "com.huawei.permissionmanager.ui.MainActivity"
        )
        if (hasActivity(context, intent)) {
            return intent
        }
        intent.setClassName(
            "com.huawei.systemmanager",
            "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity"
        )
        if (hasActivity(context, intent)) {
            return intent
        }
        intent.setClassName(
            "com.huawei.systemmanager",
            "com.huawei.notificationmanager.ui.NotificationManagmentActivity"
        )
        return if (hasActivity(context, intent)) {
            intent
        } else MdefaultApi(context)
    }

    private fun xiaomiApi(context: Context): Intent? {
        val intent = Intent("miui.intent.action.APP_PERM_EDITOR")
        intent.putExtra("extra_pkgname", context.packageName)
        if (hasActivity(context, intent)) {
            return intent
        }
        intent.setClassName(
            "com.miui.securitycenter",
            "com.miui.permcenter.permissions.AppPermissionsEditorActivity"
        )
        return if (hasActivity(context, intent)) {
            intent
        } else MdefaultApi(context)
    }

    private fun oppoApi(context: Context): Intent? {
        val intent = Intent()
        intent.putExtra("packageName", context.packageName)
        intent.setClassName(
            "com.color.safecenter",
            "com.color.safecenter.permission.floatwindow.FloatWindowListActivity"
        )
        if (hasActivity(context, intent)) {
            return intent
        }
        intent.setClassName(
            "com.coloros.safecenter",
            "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity"
        )
        if (hasActivity(context, intent)) {
            return intent
        }
        intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.PermissionAppListActivity")
        return if (hasActivity(context, intent)) {
            intent
        } else MdefaultApi(context)
    }

    private fun vivoApi(context: Context): Intent? {
        val intent = Intent()
        intent.setClassName(
            "com.iqoo.secure",
            "com.iqoo.secure.ui.phoneoptimize.FloatWindowManager"
        )
        intent.putExtra("packagename", context.packageName)
        if (hasActivity(context, intent)) {
            return intent
        }
        intent.setClassName(
            "com.iqoo.secure",
            "com.iqoo.secure.safeguard.SoftPermissionDetailActivity"
        )
        return if (hasActivity(context, intent)) {
            intent
        } else MdefaultApi(context)
    }

    private fun meizuApi(context: Context): Intent? {
        val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")
        intent.putExtra("packageName", context.packageName)
        intent.component = ComponentName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")
        return if (hasActivity(context, intent)) {
            intent
        } else MdefaultApi(context)
    }

    companion object {
        private val MARK = Build.MANUFACTURER.lowercase(Locale.getDefault())
        const val REQUEST_OVERLY = 7562
        private fun LdefaultApi(context: Context): Intent {
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            intent.data = Uri.fromParts("package", context.packageName, null)
            return intent
        }

        private fun appDetailsApi(context: Context): Intent {
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            intent.data = Uri.fromParts("package", context.packageName, null)
            return intent
        }

        private fun MdefaultApi(context: Context): Intent? {
            var intent: Intent? = null
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
            }
            intent!!.data = Uri.fromParts("package", context.packageName, null)
            return if (hasActivity(context, intent)) {
                intent
            } else appDetailsApi(context)
        }

        private fun meiZuApi(context: Context): Intent? {
            val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")
            intent.putExtra("packageName", context.packageName)
            intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")
            return if (hasActivity(context, intent)) {
                intent
            } else MdefaultApi(context)
        }

        private fun hasActivity(context: Context, intent: Intent?): Boolean {
            val packageManager = context.packageManager
            return packageManager.queryIntentActivities(
                intent!!,
                PackageManager.MATCH_DEFAULT_ONLY
            ).size > 0
        }
    }
}
权限说明

“Draw Over Other Apps”(在其他应用上层绘制)权限:

这个权限允许应用在其他应用的上层绘制悬浮窗口,例如悬浮通知、悬浮工具栏、聊天头像等。通过这个权限,应用可以在其他应用的界面上显示自己的内容,但是这些窗口通常会有一定的限制,不会覆盖系统级别的UI元素(如状态栏、导航栏等)。

“Background Pop-ups”(后台弹窗)权限:

这个权限控制应用在后台是否允许弹出窗口,即使应用处于后台运行状态。这意味着即使应用不在前台,它仍然可以显示一些弹窗、通知或者提醒。这可以让应用在后台运行时继续向用户展示重要的信息。文章来源地址https://www.toymoban.com/news/detail-685253.html

总结
  • 先判断是否是特殊机型,如果是则需要申请后台弹出界面权限
  • 如果不是特殊机型,则有两种方案,一是全屏通知,二是申请在其他应用上层绘制权限

到了这里,关于Android 后台启动Activity适配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Activity的启动流程(Android-10)

    在Android开发中,我们经常会用到startActivity(Intent)方法,但是你知道startActivity(Intent)后Activity的启动流程吗?今天就专门讲一下最基础的startActivity(Intent)看一下Activity的启动流程,同时由于Launcher的启动后续和这里基本类似,就记录在一起。注意本章都是基于Android-10来讲解的。

    2024年01月17日
    浏览(46)
  • Android Activity启动过程详解

    1,《android系统启动流程简介》 2,《android init进程启动流程》 3,《android zygote进程启动流程》 4,《Android SystemServer进程启动流程》 5,《android launcher启动流程》 6,《Android Activity启动过程详解》 1,《Android 源码下载和编译》 2,《android 11源码编译和pixel3 刷机》 3,《Andro

    2024年02月09日
    浏览(50)
  • Activity 的启动流程(Android 13)

    Activity 的启动过程分为两种:一种是普通 Activity 的启动过程,另一种是根 Activity 的启动过程。普通 Activity 指的是除应用程序启动的第一个 Activity 之外的其他 Activity。 根 Activity 指的是应用程序启动的第一个 Activity,因此,根 Activity 的启动过程一般情况下也可以理解为应用程

    2024年02月11日
    浏览(48)
  • Android Activity启动流程一:从Intent到Activity创建

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 学习前,建议有相关知识储备: 【Android 基础】 应用(Application)启动流程 通过本文你可以学习到Activity启动流

    2024年02月10日
    浏览(44)
  • Android Activity 启动流程 二:setContentView

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 接 - 上 篇,Activity创建后,还只是调用了onCreate方法,页面并没有展示出来,还需要调用setContentVie

    2024年02月10日
    浏览(46)
  • Android Framework学习之Activity启动原理

    Android 13.0 Activity启动原理逻辑流程图如下:

    2024年02月05日
    浏览(53)
  • Activity启动过程详解(Android 12源码分析)

    启动一个Activity,通常有两种情况,一种是在应用内部启动Activity,另一种是Launcher启动 1、应用内启动 通过startActivity来启动Activity 启动流程: 一、Activity启动的发起 二、Activity的管理——ATMS 三、线程切换即消息处理——mH 四、Activity启动核心实现——初始化及生命周期 2、

    2024年02月13日
    浏览(46)
  • Android入门教程之Activity(生命周期,启动...)

    Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。 Activity 1. Activity 的使用 我们新建

    2024年02月04日
    浏览(63)
  • Android实现监听APP启动、前台和后台

    前言 在我们开发的过程中,经常会遇到需要我们判断app进入后台,或者切换到前台的情况。比如我们想判断app切换到前台时,显示一个解锁界面,要求用户输入解锁密码才能继续进行操作;我们想判断app切换到后台,记录一下log;或者当用户切换回前台时,我们想刷新一下页

    2024年02月11日
    浏览(39)
  • Android 后台启动startService()相关问题的解决

    有一个用户需要这样一个功能,要求是APP能在充电的时候自动进入APP的一个界面 我寻思着,这玩意用普通权限做不了呀,不过APP有root权限倒也无妨,于是便决定采用Service去做后台服务 某天,在Bugly看到如下的错误报告 提示我无法在后台启动这个服务 于是便开始着手解决这

    2024年02月02日
    浏览(75)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包