Android 是否有一种方法可以通过编程方式定位给定应用程序中的所有窗口?

Android 是否有一种方法可以通过编程方式定位给定应用程序中的所有窗口?,android,android-view,android-window,Android,Android View,Android Window,是否可以通过编程方式枚举应用程序中的所有android.view.Windows或装饰视图 例如,对话框将在新的窗口中打开,与主活动窗口分开。我可以通过Dialog.getWindow()找到它们,但我不确定如何使用内置组件(如“活动”菜单弹出窗口)实现这一点 是否有任何方法可以从应用程序、上下文、或窗口管理器、或其他方式枚举与我的应用程序关联的窗口 我可以使用adb dumpsys window查看我的应用程序的所有窗口,但我正在寻找一种在我的应用程序中执行此操作而不需要root的方法。我找到

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

例如,
对话框
将在新的
窗口
中打开,与主活动窗口分开。我可以通过
Dialog.getWindow()
找到它们,但我不确定如何使用内置组件(如“活动”菜单弹出窗口)实现这一点

是否有任何方法可以从
应用程序
上下文
、或
窗口管理器
、或其他方式枚举与我的应用程序关联的窗口


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

我找到了一种方法,通过在
@hidden windowmanager global
上的反射来执行此操作。至少到目前为止,我知道这适用于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-7681184}

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


当然,对于任何能找到更好方法的人来说,悬赏仍然是值得一试的:)

SDK附带的工具是值得一试的。

我不完全确定这是否回答了实际问题,但这是一种更好的方法,可以获得公认答案中建议的所有根视图

如前所述,我还成功地仅使用反射实现了这一点,但此代码支持API 14及以上版本的所有版本(我在下面没有检查):

公共静态列表getWindowManagerView(){
试一试{
如果(Build.VERSION.SDK\u INT>=Build.VERSION\u code.ICE\u CREAM\u三明治&&
Build.VERSION.SDK\u INT=Build.VERSION\u code.JELLY\u BEAN\u MR1){
//从WindowManagerGlobal.mViews获取列表
类wmgClass=Class.forName(“android.view.WindowManagerGlobal”);
对象wmgInstance=wmgClass.getMethod(“getInstance”).invoke(null);
返回视图fromWM(wmgClass,wmgInstance);
}
}捕获(例外e){
e、 printStackTrace();
}
返回新的ArrayList();
}
私有静态列表视图FromWM(类wmClass,对象wmInstance)引发异常{
Field viewsField=wmClass.getDeclaredField(“mViews”);
viewsField.setAccessible(true);
对象视图=viewsField.get(wmInstance);
如果(视图实例列表){
返回(列表)viewsField.get(wmInstance);
}else if(视图实例视图[]){
返回Arrays.asList((View[])viewsField.get(wmInstance));
}
返回新的ArrayList();
}

通过访问类文件,然后将其添加到android SDK中的android.jar,您可以直接使用@hidden API,而无需使用反射。以下是方法:

具体android版本(19,21,22,23,24)的android.jar的源代码可以在这里获取:

因此,您可以直接使用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-7681184}


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

每个解决方案都在上面的Java中,我为Kotlin转换用户的答案制定了解决方案-

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 {}
试试看
{
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)作为数组
for(rootViewName中的viewName){
val rootView=getRootView.invoke(wagInstance,viewName)作为视图
}
}catch(异常:java.lang.exception{}

那些有能力使用
minSdk 29
的人可以使用。在内部,它指的是
WindowManagerGlobal
中的
mViews
属性,但可供公众使用。

活动菜单弹出窗口的窗口与活动的窗口相同,即activity.getWindow()不适合你?不幸的是,不适合。我正在运行android FingerPaint示例(在4.3上),在我点击三点菜单按钮后,我可以在监视器中看到弹出窗口在它自己的窗口中。我还可以运行“adb shell dumpsys window tokens”,并看到paint应用程序确实有两个与其相关联的窗口:allAppWindows=[window]{418f9ce8 u0 com.example.paintsample/com.example.paintsample.paintsample},窗口{41a06d08 u0 PopupWindow:41ac65a0}]对话框也一样。只是好奇,你为什么需要这些信息,或者说,一旦你有了这些信息,你打算怎么处理它?@Josh我正在编写一个库,用于在给定的应用程序中拍摄屏幕截图。只需在根视图上调用getDrawingCache就足够简单了,但为了包含像重叠对话框这样的东西,它们不是同一个窗口hiera阿奇,我不得不跳过这道障碍。哇,祝你好运,听起来很痛苦:)是我吗
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 {}