本文对Robotium的悬浮窗实现进行一点调研
Robotium本身支持悬浮窗点击,这里我们可以参考详细看下源码
与Uiautomator稍微不一样的是Robotium获取到的是Activtity的rootview,而Uiautomator直接获取的已经不是rootview了,从这个原理可以看出使用Uiautomator实现悬浮窗抓取也许是不可实现的(个人观点)
在Android中,对于一般的Activity或其对话框,其rootView称为DecorView,其实就是Activity和Dialog外面的那层框,Activity或dialog的层次可以用HierarchyViewer或者UiautomatorViewer查看,HierarchyViewer可以查看到DecorView,但是UiautomatorViewer查看到的只有Activity层了。
Robotium是如何实现获取到DecorView层源码如下:
1)在android4.2之前,获取类为android.view.WindowManagerImpl,在android4.2之后,获取类为WindowManagerGlobal。
private static Class<?> windowManager;
static{
try {
String windowManagerClassName;
if (android.os.Build.VERSION.SDK_INT >= 17) {
windowManagerClassName = "android.view.WindowManagerGlobal";
} else {
windowManagerClassName = "android.view.WindowManagerImpl";
}
windowManager = Class.forName(windowManagerClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
e.printStackTrace();
}
}
2)获得类的实例,此类是个单例类,有直接的静态变量可以获取到其实例,4.2及之后的版本其变量名称为sDefaultWindowManager,3.2至4.1,其变量名称为sWindowManager,3.2之前,变量名称为mWindowManager
private void setWindowManagerString(){
if (android.os.Build.VERSION.SDK_INT >= 17) {
windowManagerString = "sDefaultWindowManager";
} else if(android.os.Build.VERSION.SDK_INT >= 13) {
windowManagerString = "sWindowManager";
} else {
windowManagerString = "mWindowManager";
}
}
3)获取变量的值,从4.4开始类型变为ArrayList<View>,之前为View[]
public View[] getWindowDecorViews()
{
Field viewsField;
Field instanceField;
try {
viewsField = windowManager.getDeclaredField("mViews");
instanceField = windowManager.getDeclaredField(windowManagerString);
viewsField.setAccessible(true);
instanceField.setAccessible(true);
Object instance = instanceField.get(null);
View[] result;
if (android.os.Build.VERSION.SDK_INT >= 19) {
result = ((ArrayList<View>) viewsField.get(instance)).toArray(new View[0]);
} else {
result = (View[]) viewsField.get(instance);
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
4)对mViews的过滤
mViews中会包含三种类型的View:
1) 当前显示的以及没有显示的Activity的DecorView
2) 当前对话框的DecorView
3) 悬浮框View等其他不属于DecorView的独立View
在搜索控件时,显然需要在最上层界面中搜索,所以搜索范围为:
最上层的Activity/Dialog + 悬浮框
对于悬浮框,robotium中的处理是找出mViews中不属于DecorView类的View,并将其所有子控件引入。
/**
* Returns whether a view is a DecorView
* @param view
* @return true if view is a DecorView, false otherwise
*/
private boolean isDecorView(View view) {
if (view == null) {
return false;
}
final String nameOfClass = view.getClass().getName();
return (nameOfClass.equals("com.android.internal.policy.impl.PhoneWindow$DecorView") ||
nameOfClass.equals("com.android.internal.policy.impl.MultiPhoneWindow$MultiPhoneDecorView") ||
nameOfClass.equals("com.android.internal.policy.PhoneWindow$DecorView"));
}
对于Activity/Dialog的筛选,Robotium采取对比DrawingTime的方法选出最后绘制的DecorView,其即为最上层Activity/Dialog的DecorView:
/**
* Returns the most recent view container
*
* @param views the views to check
* @return the most recent view container
*/
private final View getRecentContainer(View[] views) {
View container = null;
long drawingTime = 0;
View view;
for(int i = 0; i < views.length; i++){
view = views[i];
if (view != null && view.isShown() && view.hasWindowFocus() && view.getDrawingTime() > drawingTime) {
container = view;
drawingTime = view.getDrawingTime();
}
}
return container;
}
对于悬浮窗的控件ID获取,目前没有找到一个好的工具可以获取到id或者text,用代码捕获内容,代码如下:
public void dumpAllView()
{
Log.e("","===== dump all view =====");
for(View v: getViews())
{
Log.e("", v.getId()+":"+v.getClass().getSimpleName());
Log.e("", v.toString());
}
}
http://www.tuicool.com/articles/UjuM3u
http://www.cnblogs.com/yogin/p/4061050.html