有没有办法以编程方式在给定的应用程序中找到所有Windows?

时间:2013-10-29 21:58:50

标签: android android-view android-window

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

例如,

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

是否可以使用ApplicationContextWindowManager或其他方式枚举与我的应用相关联的Windows?

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

6 个答案:

答案 0 :(得分:18)

我找到了通过@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}

     

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

对于任何能找到更好方法的人来说,赏金仍然可以抓住:)

答案 1 :(得分:12)

我不完全确定这会回答实际问题,但这是获得所有根视图的更好方法,如接受的答案所示。

如前所述,我还设法仅使用反射完成此操作,但此代码支持 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>();
}

答案 2 :(得分:4)

SDK附带的Hierarchyviewer工具非常值得用它来衡量。

答案 3 :(得分:2)

您可以直接使用@hidden API,无需通过访问类文件使用反射,然后将其添加到Android SDK中的android.jar。这是如何做: https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/

特定Android版本(19,21,22,23,24)的android.jar源代码可以在这里获取:  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 VE .... R ....... 0,0-768,1184}

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

答案 4 :(得分:0)

每个解决方案都在上面的 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 {}

答案 5 :(得分:0)

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