无悬浮窗权限实现全局Dialog

有些场景下需要显示一些提示弹窗,但把握不好弹出时机容易先弹出弹窗然后界面马上被杀掉进而看不到提示内容,例如强制下线:客户端退回登录界面并弹出提示弹窗。

如果是直接拿的栈顶activity去弹出,没有将弹窗逻辑写到具体activity中,或不好确定activty的变化就容易出现这种现象。

由于applicationContext没有AppWindowToken,所以dialog无法使用applicationContext创建,要么就使用windowManager配合WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY使用创建全局悬浮窗。但是这种做法需要申请权限。那么,在没有悬浮权限情况下如何做到让dialog不受栈顶activity变化的影响?

我的想法是通过application.registerActivityLifecycleCallbacks在activity变化时,关闭原来的弹窗,并重新创建一个一样的dialog并显示。

效果演示:

1. 栈顶界面被杀

2. 有新界面弹出

以下是代码实现:

kotlin 复制代码
/**
 * @Description 无需悬浮权限的全局弹窗,栈顶activity变化后通过反射重建,所以子类构造方法需无参
 */
open class BaseAppDialog<T : ViewModel>() : Dialog(topActivity!!.get()!!), ViewModelStoreOwner {

    companion object {
        private val TAG = BaseAppDialog::class.java.simpleName
        private var topActivity: WeakReference<Activity>? = null
        private val staticRestoreList = linkedMapOf<Class<*>, String>()
        private val staticViewModelStore: ViewModelStore = ViewModelStore()

        @JvmStatic
        fun init(application: Application) {
            application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
                override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                    topActivity = WeakReference(activity)
                }

                override fun onActivityStarted(activity: Activity) {

                }

                override fun onActivityResumed(activity: Activity) {
                    topActivity = WeakReference(activity)
                    val tempList = arrayListOf<BaseAppDialog<*>>()
                    val iterator = staticRestoreList.iterator()
                    while (iterator.hasNext()) {
                        val next = iterator.next()
                        val topName = (topActivity?.get() ?: "")::class.java.name
                        if (next.value != topName) {  //避免onCreate创建的弹窗重复弹出
                            //重新创建dialog
                            val newInstance = Class.forName(next.key.name).getConstructor().newInstance() as BaseAppDialog<*>
                            tempList.add(newInstance)
                            Log.e(TAG, "重新创建${next.key.name},于$topName")
                            iterator.remove()
                        }

                    }

                    tempList.forEach {
                        it.show()
                    }

                    if (staticRestoreList.size == 0) {
                        staticViewModelStore.clear()
                    }
                }

                override fun onActivityPaused(activity: Activity) {
                }

                override fun onActivityStopped(activity: Activity) {

                }

                override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
                }

                override fun onActivityDestroyed(activity: Activity) {
                }
            })
        }
    }


    var vm: T? = null

    init {
        val genericClass = getGenericClass()
        if (vm == null) {
            (genericClass as? Class<T>)?.let {
                vm = ViewModelProvider(this)[it]
            }
        }

        //activity暂停时关闭
        topActivity?.get()?.let {
            (it as LifecycleOwner).lifecycle.addObserver(object : DefaultLifecycleObserver {
                override fun onPause(owner: LifecycleOwner) {
                    super.onPause(owner)
                    dismissSilent()
                }
            })
        }
    }

    fun setSilentVm(viewModel: ViewModel?) {
        vm = viewModel as? T
    }


    //用于栈顶变化时的关闭
    private fun dismissSilent() {
        super.dismiss()
    }

    override fun show() {
        super.show()
        staticRestoreList.put(this::class.java, (topActivity?.get() ?: "")::class.java.name)
    }

    override fun dismiss() {
        super.dismiss()
        staticRestoreList.remove(this::class.java)
    }


    //获取泛型实际类型
    private fun getGenericClass(): Class<*>? {
        val superclass = javaClass.genericSuperclass
        if (superclass is ParameterizedType) {
            val actualTypeArguments: Array<Type>? = superclass.actualTypeArguments
            if (!actualTypeArguments.isNullOrEmpty()) {
                val type: Type = actualTypeArguments[0]
                if (type is Class<*>) {
                    return type
                }
            }
        }
        return ViewModel::class.java
    }


    //自己管理viewModel以便恢复数据
    override fun getViewModelStore(): ViewModelStore {
        return staticViewModelStore
    }
}

参数传递的话,直接通过修改dialog的viewmodel变量或调用其方法来实现。

kotlin 复制代码
class TipDialogVm : ViewModel() {
    val content = MutableLiveData<String>("")
}


class TipDialog2 : BaseAppDialog<TipDialogVm>() {

    var binding : DialogTip2Binding? = null

    init {
        binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.dialog_tip2, null, false)
        binding?.lifecycleOwner = context as? LifecycleOwner
        binding?.vm = vm
        setContentView(binding!!.root)

    }
}

弹出弹窗

kotlin 复制代码
TipDialog2().apply {
    vm?.content?.value = "嗨嗨嗨"
}.show()
相关推荐
LucianaiB31 分钟前
如何做好一份优秀的技术文档:专业指南与最佳实践
android·java·数据库
duwei_wang5 小时前
[Android]-Admob配置过多导致的慢消息
android
雨白7 小时前
发送自定义广播
android
雨白7 小时前
深入理解广播机制 (BroadcastReceiver)
android
婵鸣空啼11 小时前
GD图像处理与SESSiON
android
sunly_12 小时前
Flutter:导航固定背景图,滚动时导航颜色渐变
android·javascript·flutter
用户20187928316713 小时前
简单了解android.permission.MEDIA_CONTENT_CONTROL权限
android
_一条咸鱼_13 小时前
Android Runtime类卸载条件与资源回收策略(29)
android·面试·android jetpack
顾林海13 小时前
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
android·面试·性能优化
砖厂小工13 小时前
Now In Android 精讲 8 - Gradle build-logic 现代构建逻辑组织方式
android