无悬浮窗权限实现全局Dialog

3,228 阅读2分钟

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

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

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

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

效果演示:

1. 栈顶界面被杀

界面退出

2. 有新界面弹出

界面退出

以下是代码实现:

/**
 * @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<*>, Boolean>()    //第二个参数:是否临时关闭
        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 == true) {  //避免onCreate创建的弹窗重复弹出
                            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]
            }
        }

        topActivity?.get()?.let {
            (it as LifecycleOwner).lifecycle.addObserver(object : DefaultLifecycleObserver {
                override fun onPause(owner: LifecycleOwner) {
                    super.onPause(owner)
                    dismissSilent()
                }
            })
        }
    }


    //用于栈顶变化时的关闭
    private fun dismissSilent() {
        super.dismiss()
        staticRestoreList.replace(this::class.java, true)
    }

    override fun show() {
        super.show()
        staticRestoreList.put(this::class.java, false)
    }

    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变量或调用其方法来实现。

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)

    }
}

弹出弹窗

TipDialog2().apply {
    vm?.content?.value = "嗨嗨嗨"
}.show()