Android-如何判断Activity 上是否有弹窗?
本文最后更新于 880 天前,其中的信息可能已经有所发展或是发生改变。
  • Activity 在 attach 阶段创建了 PhoneWindow,并将 AppToken 存储到 PhoneWindow 中,然后通过 createLocalWindowManager 创建了一个本地的 WindowManager,该实例是 WindowManagerImpl,构造传入的 parentWindow 为 PhoneWindow。在 onResume 阶段时,从 PhoneWindow 中获取 WindowManager 来 addView。

  • Dialog 有自己的 PhoneWindow,但 Dialog 并没有从 PhoneWindow 中去 get WindowManager,而是直接使用 getSystemService 拿到 Activity 的 WindowManager 来 addView。

  • PopWindow 内部是通过 getSystemService 来拿到 Activity WindowManager + 内置子窗口 type 来实现的弹框。

internal class FloatingActivityLifecycle : Application.ActivityLifecycleCallbacks {

    private val mActivities = ArrayList<Activity>()

    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        mActivities.add(activity)
    }

    override fun onActivityStarted(activity: Activity) {
    }

    override fun onActivityResumed(activity: Activity) {
    }

    override fun onActivityPaused(activity: Activity) {
    }

    override fun onActivityStopped(activity: Activity) {
    }

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

    override fun onActivityDestroyed(activity: Activity) {
        mActivities.remove(activity)
    }


    fun getActivities(): List<Activity> {
        return mActivities.toList()
    }
}
object FloatingWindowManager {

    private val lifecycle by lazy {
        FloatingActivityLifecycle()
    }

    @JvmStatic
    fun init(context: Application) {
        context.registerActivityLifecycleCallbacks(lifecycle)
    }


    /**
     * WindowManagerGlobal mViews:
     *
     * 0、Ac:------
     * 1、-- Dialog
     * 2、-- Dialog
     * 3、Ac:------
     * 4、Ac:------
     */
    fun hasFloatingWindowByView(activity: Activity): Boolean {
        return getFloatWindowViewByView(activity).isNotEmpty()
    }


    fun getFloatWindowViewByView(activity: Activity): List<View> {
        val targetDecorView = activity.window.decorView
        val acDecorViews = lifecycle.getActivities().map { it.window.decorView }.toList()
        val mView = Window.getViews().map { it }.toList()
        val targetIndex = mView.first { it == targetDecorView }
        val index = mView.indexOf(targetIndex)
        val floatView = arrayListOf<View>()
        for (i in index + 1 until mView.size) {
            if (acDecorViews.contains(mView[i])) {
                break
            }
            floatView.add(mView[i])
        }
        return floatView
    }


    fun hasFloatWindowByToken(activity: Activity): Boolean {
        // 获取目标 Activity 的 decorView
        val targetDecorView = activity.window.decorView
        // 获取目标 Activity 的 子窗口的 token
        val targetSubToken = targetDecorView.windowToken


        //  拿到 mView 集合,找到目标 Activity 所在的 index 位置
        val mView = Window.getViews().map { it }.toList()
        val targetIndex = mView.indexOfFirst { it == targetDecorView }

        // 获取 mParams 集合
        val mParams = Window.getParams()
        // 根据目标 index 从 mParams 集合中找到目标 token
        val targetToken = mParams[targetIndex].token


        // 遍历判断时,目标 Activity 自己不能包括,所以 size 需要大于 1
        return mParams
            .map { it.token }
            .filter { it == targetSubToken || it == null || it == targetToken }
            .size > 1
    }


    fun getFloatWindowViewByToken(activity: Activity): List<View> {
        // 获取目标 Activity 的 decorView
        val targetDecorView = activity.window.decorView
        // 获取目标 Activity 的 子窗口的 token
        val targetSubToken = targetDecorView.windowToken


        //  拿到 mView 集合,找到目标 Activity 所在的 index 位置
        val mView = Window.getViews().map { it }.toList()
        val targetIndex = mView.indexOfFirst { it == targetDecorView }

        // 获取 mParams 集合
        val mParams = Window.getParams()
        // 根据目标 index 从 mParams 集合中找到目标 token
        val targetToken = mParams[targetIndex].token


        val floatView = arrayListOf<View>()

        mParams.forEachIndexed { index, params ->
            val token = params.token
            // Activity 自身不参与
            if (index != targetIndex) {
                if (token == targetSubToken || token == null || token == targetToken) {
                    // 根据 index 拿到 mView 中的 View
                    floatView.add(mView[index])
                }
            }
        }
        return floatView
    }


}
internal object Window {

    private val windowManagerClass by lazy(LazyThreadSafetyMode.NONE) {
        val className =
            "android.view.WindowManagerGlobal"
        try {
            Class.forName(className)
        } catch (ignored: Throwable) {
            Log.w("WindowManagerSpy", ignored)
            null
        }
    }

    private val windowManagerInstance by lazy(LazyThreadSafetyMode.NONE) {
        windowManagerClass?.let { windowManagerClass ->
            val methodName =
                "getInstance"
            windowManagerClass.getMethod(methodName).invoke(null)
        }
    }


    private val mViewsField by lazy(LazyThreadSafetyMode.NONE) {
        windowManagerClass?.let { windowManagerClass ->
            windowManagerClass.getDeclaredField("mViews").apply { isAccessible = true }
        }
    }


    private val mParams by lazy(LazyThreadSafetyMode.NONE) {
        windowManagerClass?.let { windowManagerClass ->
            windowManagerClass.getDeclaredField("mParams").apply { isAccessible = true }
        }
    }



    @SuppressLint("PrivateApi", "ObsoleteSdkInt", "DiscouragedPrivateApi")
    fun getViews(): List<View> {
        try {
            windowManagerInstance?.let { windowManagerInstance ->
                mViewsField?.let { mViewsField ->
                    return mViewsField[windowManagerInstance] as ArrayList<View>
                }
            }
        } catch (ignored: Throwable) {
            Log.w("WindowManagerSpy", ignored)
        }
        return arrayListOf()
    }

    @SuppressLint("PrivateApi", "ObsoleteSdkInt", "DiscouragedPrivateApi")
    fun getParams(): List<WindowManager.LayoutParams> {
        try {
            windowManagerInstance?.let { windowManagerInstance ->
                mParams?.let { mViewsField ->
                    return mViewsField[windowManagerInstance] as ArrayList<WindowManager.LayoutParams>
                }
            }
        } catch (ignored: Throwable) {
            Log.w("WindowManagerSpy", ignored)
        }
        return arrayListOf()
    }

}

源github地址
fork地址

本文链接:https://iichen.cn/?p=417
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇