是否可以以编程方式枚举应用程序中的所有android.view.Window
或装饰视图?
Dialogs
将在新的Window
中打开,与主Activity窗口分开。我可以通过Dialog.getWindow()
找到它们,但我不确定如何使用内置组件(例如活动菜单弹出窗口)来执行此操作。
是否可以使用Application
,Context
或WindowManager
或其他方式枚举与我的应用相关联的Windows?
我可以使用adb dumpsys window
查看我的所有应用程序窗口,但我正在寻找一种方法在我的应用程序中执行此操作而不需要root。
答案 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
属性,但可供公众使用。