1. 问题背景描述
在工作中需要在没有项目源码的情况下直接使用robotium测试目标android平台launcher,平台的版本基于当前最新的android 4.4.2。之前在验证可行性的时候使用本人同样使用android4.4.2的测试手机htc incredable s针对一个只有apk的notepad应用做过同样的验证,在测试手机上运行完全没有问题。该测试代码如下:
成都创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站建设、网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的河池网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
package com.example.android.notepad.tryout;  import com.robotium.solo.Solo;  import android.test.ActivityInstrumentationTestCase2; import android.widget.TextView; import android.app.Activity;  @SuppressWarnings("rawtypes") public class NotePadTest extends ActivityInstrumentationTestCase2{  	private static Solo solo = null; 	public Activity activity; 	 	private static final int NUMBER_TOTAL_CASES = 2; 	private static int run = 0; 	 	private static Class> launchActivityClass; 	//对应re-sign.jar生成出来的信息框里的两个值 	private static String mainActiviy = "com.example.android.notepad.NotesList"; 	private static String packageName = "com.example.android.notepad";  	static {  		try {  			launchActivityClass = Class.forName(mainActiviy);  		} catch (ClassNotFoundException e) {  			throw new RuntimeException(e);  		}  	} 	 	 	@SuppressWarnings("unchecked") 	public NotePadTest() { 		super(packageName, launchActivityClass); 	}  	 	@Override 	public void setUp() throws Exception { 		//setUp() is run before a test case is started.  		//This is where the solo object is created. 		super.setUp();  		//The variable solo has to be static, since every time after a case's finished, this class TCCreateNote would be re-instantiated 		// which would lead to soto to re-instantiated to be null if it's not set as static 		//TextView title = (TextView)getActivity().findViewById(Ref.id.title); 		 		if(solo == null) { 			 			NotePadTest.solo = new Solo(getInstrumentation(),getActivity()); 			 		} 	} 	 	@Override 	public void tearDown() throws Exception { 		//Check whether it's the last case executed. 		run += countTestCases(); 		if(run >= NUMBER_TOTAL_CASES) { 			solo.finishOpenedActivities(); 		} 	}  	public void testAddNoteCNTitle() throws Exception { 		//Thread.sleep(5000); 		solo.clickOnMenuItem("Add note"); 		solo.enterText(0, "中文标签笔记"); 		solo.clickOnMenuItem("Save"); 		solo.clickInList(0); 		solo.clearEditText(0); 		solo.enterText(0, "Text 1"); 		solo.clickOnMenuItem("Save"); 		solo.assertCurrentActivity("Expected NotesList Activity", "NotesList"); 		 		solo.clickLongOnText("中文标签笔记"); 		solo.clickOnText("Delete"); 		 		 	} 	 	 	public void testAddNoteEngTitle() throws Exception { 		solo.clickOnMenuItem("Add note"); 		solo.enterText(0, "English Title Note"); 		solo.clickOnMenuItem("Save"); 		solo.clickInList(0); 		solo.clearEditText(0); 		solo.enterText(0, "Text 1"); 		solo.clickOnMenuItem("Save"); 		solo.assertCurrentActivity("Expected NotesList Activity", "NotesList"); 		 		solo.clickLongOnText("English Title Note"); 		solo.clickOnText("Delete"); 	} } 	@Override 	public void setUp() throws Exception { 		//setUp() is run before a test case is started.  		//This is where the solo object is created. 		super.setUp();  		//The variable solo has to be static, since every time after a case's finished, this class TCCreateNote would be re-instantiated 		// which would lead to soto to re-instantiated to be null if it's not set as static 		 		if(solo == null) {		 			NotePadTest.solo = new Solo(getInstrumentation(),getActivity());	 		} 	}当时一直怀疑是否系统launcher的robotium初始化和setup方法跟普通的apk不一样,google上有历史文章描述getActivity()在Android 2.xx.xx上确实有这个问题,但后来的版本已经解决,而本人使用的时当前最的4.4.2,所以不应该还存在这种问题。针对这个思路去尝试找解决办法终无果。
2.问题分析
/** * Utility method for launching an activity with a specific Intent. * *NOTE: The parameter pkg must refer to the package identifier of the * package hosting the activity to be launched, which is specified in the AndroidManifest.xml * file. This is not necessarily the same as the java package name. * * @param pkg The package hosting the activity to be launched. * @param activityCls The activity class to launch. * @param intent The intent to launch with * @return The activity, or null if non launched. */ @SuppressWarnings("unchecked") public final
T launchActivityWithIntent( String pkg, Class activityCls, Intent intent) { intent.setClassName(pkg, activityCls.getName()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); T activity = (T) getInstrumentation().startActivitySync(intent); getInstrumentation().waitForIdleSync(); return activity; } 
导致挂起的位置是里面的getInstrumentation().waitForIdleSync()方法,到了这里再代码跟踪进去看到的就是android.app.instrumentation这个基类里面:
    /**      * Synchronously wait for the application to be idle.  Can not be called      * from the main application thread -- use {@link #start} to execute      * instrumentation in its own thread.      */     public void waitForIdleSync() {         validateNotAppThread();         Idler idler = new Idler(null);         mMessageQueue.addIdleHandler(idler);         mThread.getHandler().post(new EmptyRunnable());         idler.waitForIdle();     }这里按照本人的理解做的事情大概如下:- 首先确保调用这个方法的来源不是application的主线程
- 然后把当前等待application变成idle的请求放到消息队列中
- 最后等待app在处理完所有事件达到idle状态的时候返回
- 如果目标activity没有起来,那么启动该activity并放在前台
- 如果目标activity已经起来,那么直接放在前台等待被测试
- 如果该该activity所属application在自动不停的接受事件,直接调用getActivity会因为一直等待application变成idle状态而挂起
3. 解决方法
solo = new Solo(getInstrumentation());因为我们的launcher在robotium在kill掉原来的launcher进程的时候就会自动起来,所以并不需要手动的去getActivity()去启动。这种方法在不能启动起来的apk如notepad上面就不行,不信你去掉getActivity()的调用,保证notepad不会启动或者放到前台。但是如果你在开始测试前先把notepad手动起来并放到前台,测试还是会正常进行的。比如以下的验证性代码:
package com.example.android.notepad.tryout;  import com.robotium.solo.Solo;  import android.test.ActivityInstrumentationTestCase2; import android.widget.TextView; import android.app.Activity;  @SuppressWarnings("rawtypes") public class NotePadTest extends ActivityInstrumentationTestCase2{  	private static Solo solo = null; 	public Activity activity; 	 	private static final int NUMBER_TOTAL_CASES = 2; 	private static int run = 0; 	 	private static Class> launchActivityClass; 	//对应re-sign.jar生成出来的信息框里的两个值 	private static String mainActiviy = "com.example.android.notepad.NotesList"; 	private static String packageName = "com.example.android.notepad";  	static {  		try {  			launchActivityClass = Class.forName(mainActiviy);  		} catch (ClassNotFoundException e) {  			throw new RuntimeException(e);  		}  	} 	 	 	@SuppressWarnings("unchecked") 	public NotePadTest() { 		super(packageName, launchActivityClass); 	}  	 	@Override 	public void setUp() throws Exception { 		//setUp() is run before a test case is started.  		//This is where the solo object is created. 		super.setUp();  		//The variable solo has to be static, since every time after a case's finished, this class TCCreateNote would be re-instantiated 		// which would lead to soto to re-instantiated to be null if it's not set as static 		//TextView title = (TextView)getActivity().findViewById(Ref.id.title); 		 		if(solo == null) { 			 			NotePadTest.solo = new Solo(getInstrumentation());//, getActivity()); 			 		} 	} 	 	@Override 	public void tearDown() throws Exception { 		//Check whether it's the last case executed. 		run += countTestCases(); 		if(run >= NUMBER_TOTAL_CASES) { 			solo.finishOpenedActivities(); 		} 	}  	public void testAddNoteCNTitle() throws Exception { 		//getActivity(); 		Thread.sleep(5000); 		solo.clickOnMenuItem("Add note"); 		solo.enterText(0, "中文标签笔记"); 		solo.clickOnMenuItem("Save"); 		solo.clickInList(0); 		solo.clearEditText(0); 		solo.enterText(0, "Text 1"); 		solo.clickOnMenuItem("Save"); 		solo.assertCurrentActivity("Expected NotesList Activity", "NotesList"); 		 		solo.clickLongOnText("中文标签笔记"); 		solo.clickOnText("Delete"); 		 		 	} }初始化solo的时候和testcase里面都没有去调用getActivity(),但是在testcase开始前先睡眠5秒,如果在这5秒的过程中你手动把notepad给启动起来,那么睡眠时间过后测试会继续正常运行。@Override     public MyActivity getActivity() {         if (mActivity == null) {             Intent intent = new Intent(getInstrumentation().getTargetContext(), MyActivity.class);             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);             // register activity that need to be monitored.             monitor = getInstrumentation().addMonitor(MyActivity.class.getName(), null, false);             getInstrumentation().getTargetContext().startActivity(intent);             mActivity = (MyActivity) getInstrumentation().waitForMonitor(monitor);             setActivity(mActivity);         }         return mActivity;     }鉴于本人现在只是做前期的可行×××,够用就好,且周末手头上也没有目标机器在手进行验证,所以有兴趣的朋友就自己去尝试下吧,| 作者 | 自主博客 | 微信 | CSDN | 
| 天地会珠海分舵 | http://techgogogo.com | 服务号:TechGoGoGo 扫描码: 
 | http://www.cqwzjz.cn/article/jdcgjs.html | 

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询 
 
