有没有办法以编程方式查找给定应用程序中的所有窗口



是否可以以编程方式枚举应用程序中的所有android.view.Window或装饰视图?

例如,Dialogs都将在 新Window 中打开,与主活动窗口分开。我可以通过Dialog.getWindow()找到它们,但我不确定如何使用内置组件(例如活动菜单弹出窗口)来做到这一点。

有什么方法,从ApplicationContextWindowManager或其他东西中枚举与我的应用程序关联的 Windows?

我可以用adb dumpsys window看到应用程序的所有窗口,但我正在寻找一种在不需要 root 的情况下在我的应用程序中执行此操作的方法。

>我找到了一种方法,通过对@hidden WindowManagerGlobal的反思来做到这一点。至少到目前为止,我知道这适用于 android-18。

private void logRootViews() {
    try {
        Class wmgClass = Class.forName("android.view.WindowManagerGlobal");                        
        Object wmgInstnace = wmgClass.getMethod("getInstance").invoke(null, (Object[])null);
        Method getViewRootNames = wmgClass.getMethod("getViewRootNames"); 
        Method getRootView = wmgClass.getMethod("getRootView", String.class);
        String[] rootViewNames = (String[])getViewRootNames.invoke(wmgInstnace, (Object[])null);
        for(String viewName : rootViewNames) {
            View rootView = (View)getRootView.invoke(wmgInstnace, viewName);
            Log.i(TAG, "Found root view: " + viewName + ": " + rootView);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出:

找到的根视图: com.example.paintsample/com.example.paintsample.PaintSample/android.view.ViewRootImpl@41deeff0: com.android.internal.policy.impl.PhoneWindow$DecorView{41dcc278 V.E.....R.......0,0-768,1184}

找到的根视图: 弹出窗口:42887380/android.view.ViewRootImpl@42891820: android.widget.PopupWindow$PopupViewContainer{42891450 V.E..... ........0,0-424,618}

当然,赏金仍然可供任何能找到更好方法的人争夺:)

我不完全确定这是否回答了实际问题,但这是按照接受答案中的建议获取所有根视图的更好方法。

如前所述,我也设法仅使用反射来完成此操作,除了此代码支持 API 14 及更高版本的所有版本(我没有在下面检查):

public static List<View> getWindowManagerViews() {
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
                Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            // get the list from WindowManagerImpl.mViews
            Class wmiClass = Class.forName("android.view.WindowManagerImpl");
            Object wmiInstance = wmiClass.getMethod("getDefault").invoke(null);
            return viewsFromWM(wmiClass, wmiInstance);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            // get the list from WindowManagerGlobal.mViews
            Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
            Object wmgInstance = wmgClass.getMethod("getInstance").invoke(null);
            return viewsFromWM(wmgClass, wmgInstance);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return new ArrayList<View>();
}
private static List<View> viewsFromWM(Class wmClass, Object wmInstance) throws Exception {
    Field viewsField = wmClass.getDeclaredField("mViews");
    viewsField.setAccessible(true);
    Object views = viewsField.get(wmInstance);
    if (views instanceof List) {
        return (List<View>) viewsField.get(wmInstance);
    } else if (views instanceof View[]) {
        return Arrays.asList((View[])viewsField.get(wmInstance));
    }
    return new ArrayList<View>();
}
SDK

附带的Hierarchyviewer工具物有所值。

你可以直接使用@hidden API,而无需通过访问类文件并在Android SDK中添加到你的android.jar中使用反射。方法如下:https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/

安卓的源代码.jar对于特定的安卓版本(19,21,22,23,24)可以在这里获取: https://github.com/anggrayudi/android-hidden-api

因此,您可以直接使用WindowManagerGlobal类来获取所有根视图,例如,

private void logRootViews() {
    WindowManagerGlobal windowManagerGlobal = WindowManagerGlobal.getInstance();
    String[] rootViewNames = windowManagerGlobal.getViewRootNames();
    for (String viewName : rootViewNames) {
        View rootView = windowManagerGlobal.getRootView(viewName);
        Log.i("", "Root view is: " + viewName + ": " + rootView);
        /*do what you want with the rootView*/
    }
}

输出:

根视图是: com.example.paintsample/

com.example.paintsample.PaintSample/android.view.ViewRootImpl@41deeff0: com.android.internal.policy.impl.PhoneWindow$DecorView{41dcc278 V.E.....R.......0,0-768,1184}

根视图是: PopupWindow:42887380/android.view.ViewRootImpl@42891820: android.widget.PopupWindow$PopupViewContainer{42891450 V.E..... ........0,0-424,618}

那些

负担得起minSdk 29的人可以使用WindowInspector.getGlobalWindowViews()。在内部,它指的是mViews WindowManagerGlobal的财产,但可供公众使用。

上面的每个解决方案都是在 Java 中,我为 Kotlin 制作了转换 Andrew Lavers 答案的解决方案-

try
    {
        val wmgClass = Class.forName("android.view.WindowManagerGlobal")
        val wagInstance = wmgClass.getMethod("getInstance").invoke(null)
        val getViewRootNames: Method = wmgClass.getMethod("getViewRootNames")
        val getRootView: Method = wmgClass.getMethod("getRootView", String::class.java)
        val rootViewNames = getViewRootNames.invoke(wagInstance) as Array<String>
        for (viewName in rootViewNames) {
            val rootView = getRootView.invoke(wagInstance, viewName) as View
        }
    } catch (exception: java.lang.Exception {}

相关内容

  • 没有找到相关文章

最新更新