Robotium学习笔记

使用Robotium有一段时间了,最近学习了它的源码和一些前辈们的分析,有所收获,在这里做个记录.

一.如何获取View控件

要想获得控件,必须先得到Activity的rootView。在Android中,对于一般的Activity或其对话框,其rootView叫做DecorView,其实就是Activity和Dialog外面的那层框(关于Activity或dialog的层次可以用HierarchyViewer来查看)

虽然通过Activity类的getWindow().getDecorView可以获取到Activity自身的DecorView,但是无法获取到对话框的,因此Robotium中界面控件是从WindowManagerGlobal(或WindowManagerImpl)中的mViews获取到的。当然mViews中不但包含DecorView,还包含同进程内的所有界面的根节(如悬浮框的根节点)。


获取mView的方法主要是在ViewFetcher类中,简单记录一下getViews方法获取mView的过程,

首先,在getAllViews()方法,其中使用了getWindowDecorViews(),在这个方法中使用反射的机制拿到WindowManager类中mView属性的值(mView相当于一个大容器,包含同进程内的所有界面的根节),具体方法如下:

public View[] getWindowDecorViews()
	{

		Field viewsField;
		Field instanceField;
		try {
                        //获取windowManager类中的mView属性
                        viewsField = windowManager.getDeclaredField("mViews");
                        //windowManager类是单例模式,获取实例对象属性
                        instanceField = windowManager.getDeclaredField(windowManagerString);
			viewsField.setAccessible(true);
			instanceField.setAccessible(true);
                        //传入一个null来获取静态实例对象
                        Object instance = instanceField.get(null);
			View[] result;
			if (android.os.Build.VERSION.SDK_INT >= 19) {
	                       //通过windowManager的实例 获取实例中mView属性的值			
                               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;
	}

以上代码中,首先使用反射获取windowManager类中的mView属性,在获取windowManager类的实例,进而得到了实例中mView属性的值

其中windowManager在android不同版本使用不同的类名,定义如下:

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();
		}
	}

windowManager类中定义的实例对象名也因android版本不同而有所变化

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";
		}
	}

到此我们已经得到了所有界面的大容器,下一步需要从中筛选出当前显示的页面DecorView()

mView中包含三类View分别是:

1) 当前显示的以及没有显示的Activity的DecorView

2) 当前对话框的DecorView

3) 悬浮框View等其他不属于DecorView的独立View


下面调用getNotDecorView方法筛选所有不是decorViews的view,并接将他们的子控件筛选出来


private final View[] getNonDecorViews(View[] views) {
		View[] decorViews = null;

		if(views != null) {
			decorViews = new View[views.length];

			int i = 0;
			View view;

			for (int j = 0; j < views.length; j++) {
				view = views[j];
				if (view != null && !(view.getClass().getName()
						.equals("com.android.internal.policy.impl.PhoneWindow$DecorView"))) {
					decorViews[i] = view;
					i++;
				}
			}
		}
		return decorViews;
	}

对于Activity/Dialog的筛选,使用getRecentDecorView()方法,找到一个最后创建的DrcotView(即为最上层Activity/Dialog的DecorView)

public final View getRecentDecorView(View[] views) {
		if(views == null)
			return null;

		final View[] decorViews = new View[views.length];
		int i = 0;
		View view;

		for (int j = 0; j < views.length; j++) {
			view = views[j];
			if (view != null){ 
				String nameOfClass = view.getClass().getName();
				if(nameOfClass.equals("com.android.internal.policy.impl.PhoneWindow$DecorView") || nameOfClass
						.equals("com.android.internal.policy.impl.MultiPhoneWindow$MultiPhoneDecorView")) {
					decorViews[i] = view;
					i++;
				}
			}
		}
		return getRecentContainer(decorViews);
	}

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;
	}


在这里如果是获取所有控件,onlySufficientlyVisible传入的值是false,所有创建view列表时不会检查控件是否显示在屏幕上,如果onlySufficientlyVisible是ture那么在加入列表时会对控件的可见性进行检查(在这的可可见性是指由于界面滚动而导致的没有显示或显示不完全的可见性)

private void addChildren(ArrayList<View> views, ViewGroup viewGroup, boolean onlySufficientlyVisible) {
		if(viewGroup != null){
			for (int i = 0; i < viewGroup.getChildCount(); i++) {
				final View child = viewGroup.getChildAt(i);

				if(onlySufficientlyVisible && isViewSufficientlyShown(child))
					views.add(child);

				else if(!onlySufficientlyVisible)
					views.add(child);

				if (child instanceof ViewGroup) {
					addChildren(views, (ViewGroup) child, onlySufficientlyVisible);
				}
			}
		}
	}

目前为止我们已经得到了所有的View



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值