Robtinum的get Activity

来源:互联网 发布:java date时间差 编辑:程序博客网 时间:2024/04/25 08:20

1. 问题背景描述

在工作中需要在没有项目源码的情况下直接使用robotium测试目标android平台launcher,平台的版本基于当前最新的android 4.4.2。之前在验证可行性的时候使用本人同样使用android4.4.2的测试手机htc incredable s针对一个只有apk的notepad应用做过同样的验证,在测试手机上运行完全没有问题。该测试代码如下:

[java] view plaincopyprint?
  1. package com.example.android.notepad.tryout;  
  2.   
  3. import com.robotium.solo.Solo;  
  4.   
  5. import android.test.ActivityInstrumentationTestCase2;  
  6. import android.widget.TextView;  
  7. import android.app.Activity;  
  8.   
  9. @SuppressWarnings("rawtypes")  
  10. public class NotePadTest extends ActivityInstrumentationTestCase2{  
  11.   
  12.     private static Solo solo = null;  
  13.     public Activity activity;  
  14.       
  15.     private static final int NUMBER_TOTAL_CASES = 2;  
  16.     private static int run = 0;  
  17.       
  18.     private static Class<?> launchActivityClass;  
  19.     //对应re-sign.jar生成出来的信息框里的两个值  
  20.     private static String mainActiviy = "com.example.android.notepad.NotesList";  
  21.     private static String packageName = "com.example.android.notepad";  
  22.   
  23.     static {  
  24.   
  25.         try {  
  26.   
  27.             launchActivityClass = Class.forName(mainActiviy);  
  28.   
  29.         } catch (ClassNotFoundException e) {  
  30.   
  31.             throw new RuntimeException(e);  
  32.   
  33.         }  
  34.   
  35.     }  
  36.       
  37.       
  38.     @SuppressWarnings("unchecked")  
  39.     public NotePadTest() {  
  40.         super(packageName, launchActivityClass);  
  41.     }  
  42.   
  43.       
  44.     @Override  
  45.     public void setUp() throws Exception {  
  46.         //setUp() is run before a test case is started.   
  47.         //This is where the solo object is created.  
  48.         super.setUp();   
  49.         //The variable solo has to be static, since every time after a case's finished, this class TCCreateNote would be re-instantiated  
  50.         // which would lead to soto to re-instantiated to be null if it's not set as static  
  51.         //TextView title = (TextView)getActivity().findViewById(Ref.id.title);  
  52.           
  53.         if(solo == null) {  
  54.               
  55.             NotePadTest.solo = new Solo(getInstrumentation(),getActivity());  
  56.               
  57.         }  
  58.     }  
  59.       
  60.     @Override  
  61.     public void tearDown() throws Exception {  
  62.         //Check whether it's the last case executed.  
  63.         run += countTestCases();  
  64.         if(run >= NUMBER_TOTAL_CASES) {  
  65.             solo.finishOpenedActivities();  
  66.         }  
  67.     }  
  68.   
  69.     public void testAddNoteCNTitle() throws Exception {  
  70.         //Thread.sleep(5000);   
  71.         solo.clickOnMenuItem("Add note");  
  72.         solo.enterText(0"中文标签笔记");  
  73.         solo.clickOnMenuItem("Save");  
  74.         solo.clickInList(0);  
  75.         solo.clearEditText(0);  
  76.         solo.enterText(0"Text 1");  
  77.         solo.clickOnMenuItem("Save");  
  78.         solo.assertCurrentActivity("Expected NotesList Activity""NotesList");  
  79.           
  80.         solo.clickLongOnText("中文标签笔记");  
  81.         solo.clickOnText("Delete");  
  82.           
  83.           
  84.     }  
  85.       
  86.       
  87.     public void testAddNoteEngTitle() throws Exception {  
  88.         solo.clickOnMenuItem("Add note");  
  89.         solo.enterText(0"English Title Note");  
  90.         solo.clickOnMenuItem("Save");  
  91.         solo.clickInList(0);  
  92.         solo.clearEditText(0);  
  93.         solo.enterText(0"Text 1");  
  94.         solo.clickOnMenuItem("Save");  
  95.         solo.assertCurrentActivity("Expected NotesList Activity""NotesList");  
  96.           
  97.         solo.clickLongOnText("English Title Note");  
  98.         solo.clickOnText("Delete");  
  99.     }  
  100. }  


但在工作的测试目标平台launcher中使用同样的方法去setup并运行简单的测试时碰到问题:测试程序一直挂起没有返回,程序挂起在以下getaActivity()方法(因是公司代码,故以notepad测试代码取代之):
[java] view plaincopyprint?
  1. @Override  
  2. public void setUp() throws Exception {  
  3.     //setUp() is run before a test case is started.   
  4.     //This is where the solo object is created.  
  5.     super.setUp();   
  6.     //The variable solo has to be static, since every time after a case's finished, this class TCCreateNote would be re-instantiated  
  7.     // which would lead to soto to re-instantiated to be null if it's not set as static  
  8.       
  9.     if(solo == null) {        
  10.         NotePadTest.solo = new Solo(getInstrumentation(),getActivity());      
  11.     }  
  12. }  

当时一直怀疑是否系统launcher的robotium初始化和setup方法跟普通的apk不一样,google上有历史文章描述getActivity()在Android 2.xx.xx上确实有这个问题,但后来的版本已经解决,而本人使用的时当前最的4.4.2,所以不应该还存在这种问题。针对这个思路去尝试找解决办法终无果。

2.问题分析

既然是getActvity()方法出现问题,而该方法原有的bug也已经在最新的版本fixed,在google无所获的情况下也只能剩下分析源码这条路了。因为是自己刚在backbook上搭建的自动化研究平台,为了节省时间,当时没有下载android的相应源码,只有sdk,所以第一步必须是先在项目中配置使用上android的源码,其理与配置javadoc相近,请查看本人之前的一篇文章《How to Configure Javadoc for Robotium Library》,这里不做累术。

加入源码后调试分析,最终程序挂起在android.test.InstrumentationTestCase中的launchActivityWithIntent方法中,以下是eclipse中的调试截图示例:

以下是该方法的完整代码片段:
[java] view plaincopyprint?
  1. /** 
  2.      * Utility method for launching an activity with a specific Intent. 
  3.      *  
  4.      * <p><b>NOTE:</b> The parameter <i>pkg</i> must refer to the package identifier of the 
  5.      * package hosting the activity to be launched, which is specified in the AndroidManifest.xml 
  6.      * file.  This is not necessarily the same as the java package name. 
  7.      * 
  8.      * @param pkg The package hosting the activity to be launched. 
  9.      * @param activityCls The activity class to launch. 
  10.      * @param intent The intent to launch with 
  11.      * @return The activity, or null if non launched. 
  12.      */  
  13.     @SuppressWarnings("unchecked")  
  14.     public final <T extends Activity> T launchActivityWithIntent(  
  15.             String pkg,  
  16.             Class<T> activityCls,  
  17.             Intent intent) {  
  18.         intent.setClassName(pkg, activityCls.getName());  
  19.         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  20.         T activity = (T) getInstrumentation().startActivitySync(intent);  
  21.         getInstrumentation().waitForIdleSync();  
  22.         return activity;  
  23.     }  

导致挂起的位置是里面的getInstrumentation().waitForIdleSync()方法,到了这里再代码跟踪进去看到的就是android.app.instrumentation这个基类里面:
[java] view plaincopyprint?
  1. /** 
  2.  * Synchronously wait for the application to be idle.  Can not be called 
  3.  * from the main application thread -- use {@link #start} to execute 
  4.  * instrumentation in its own thread. 
  5.  */  
  6. public void waitForIdleSync() {  
  7.     validateNotAppThread();  
  8.     Idler idler = new Idler(null);  
  9.     mMessageQueue.addIdleHandler(idler);  
  10.     mThread.getHandler().post(new EmptyRunnable());  
  11.     idler.waitForIdle();  
  12. }  
这里按照本人的理解做的事情大概如下:
  • 首先确保调用这个方法的来源不是application的主线程
  • 然后把当前等待application变成idle的请求放到消息队列中
  • 最后等待app在处理完所有事件达到idle状态的时候返回
看到这里我幡然领悟,在目标平台上面我们有一个天气预报的功能,在不停的发送事件给application(也就是launcher)来更新当前的天气情况,所以一直没有达到idle的状态,这样这个函数也就一直没有返回而挂起了。而在本人的测试手机上测试的notepad这个apk,一进去的launchable activity就是idle的,所以不会碰到这个问题。

带着这个思路在调整google关键字在stackoverflow中找到了国外同行碰到的一个类似的问题:http://stackoverflow.com/questions/20860832/why-does-getactivity-block-during-junit-test-when-custom-imageview-calls-start
这里总结下本人研究过程中了解到的robotium初始化solo的时候new Solo(getInstrumentation(),getActivity())中getActivity所做的事情:
  • 如果目标activity没有起来,那么启动该activity并放在前台
  • 如果目标activity已经起来,那么直接放在前台等待被测试
  • 如果该该activity所属application在自动不停的接受事件,直接调用getActivity会因为一直等待application变成idle状态而挂起

3. 解决方法

本人按照项目中的目标测试launcher的实际情况想到的解决方法是在初始化solo的时候不去调用getActivity()这个InstrumentationTestCase2的方法:
[java] view plaincopyprint?
  1. solo = new Solo(getInstrumentation());  
因为我们的launcher在robotium在kill掉原来的launcher进程的时候就会自动起来,所以并不需要手动的去getActivity()去启动。这种方法在不能启动起来的apk如notepad上面就不行,不信你去掉getActivity()的调用,保证notepad不会启动或者放到前台。但是如果你在开始测试前先把notepad手动起来并放到前台,测试还是会正常进行的。比如以下的验证性代码:
[java] view plaincopyprint?
  1. package com.example.android.notepad.tryout;  
  2.   
  3. import com.robotium.solo.Solo;  
  4.   
  5. import android.test.ActivityInstrumentationTestCase2;  
  6. import android.widget.TextView;  
  7. import android.app.Activity;  
  8.   
  9. @SuppressWarnings("rawtypes")  
  10. public class NotePadTest extends ActivityInstrumentationTestCase2{  
  11.   
  12.     private static Solo solo = null;  
  13.     public Activity activity;  
  14.       
  15.     private static final int NUMBER_TOTAL_CASES = 2;  
  16.     private static int run = 0;  
  17.       
  18.     private static Class<?> launchActivityClass;  
  19.     //对应re-sign.jar生成出来的信息框里的两个值  
  20.     private static String mainActiviy = "com.example.android.notepad.NotesList";  
  21.     private static String packageName = "com.example.android.notepad";  
  22.   
  23.     static {  
  24.   
  25.         try {  
  26.   
  27.             launchActivityClass = Class.forName(mainActiviy);  
  28.   
  29.         } catch (ClassNotFoundException e) {  
  30.   
  31.             throw new RuntimeException(e);  
  32.   
  33.         }  
  34.   
  35.     }  
  36.       
  37.       
  38.     @SuppressWarnings("unchecked")  
  39.     public NotePadTest() {  
  40.         super(packageName, launchActivityClass);  
  41.     }  
  42.   
  43.       
  44.     @Override  
  45.     public void setUp() throws Exception {  
  46.         //setUp() is run before a test case is started.   
  47.         //This is where the solo object is created.  
  48.         super.setUp();   
  49.         //The variable solo has to be static, since every time after a case's finished, this class TCCreateNote would be re-instantiated  
  50.         // which would lead to soto to re-instantiated to be null if it's not set as static  
  51.         //TextView title = (TextView)getActivity().findViewById(Ref.id.title);  
  52.           
  53.         if(solo == null) {  
  54.               
  55.             NotePadTest.solo = new Solo(getInstrumentation());//, getActivity());  
  56.               
  57.         }  
  58.     }  
  59.       
  60.     @Override  
  61.     public void tearDown() throws Exception {  
  62.         //Check whether it's the last case executed.  
  63.         run += countTestCases();  
  64.         if(run >= NUMBER_TOTAL_CASES) {  
  65.             solo.finishOpenedActivities();  
  66.         }  
  67.     }  
  68.   
  69.     public void testAddNoteCNTitle() throws Exception {  
  70.         //getActivity();   
  71.         Thread.sleep(5000);  
  72.         solo.clickOnMenuItem("Add note");  
  73.         solo.enterText(0"中文标签笔记");  
  74.         solo.clickOnMenuItem("Save");  
  75.         solo.clickInList(0);  
  76.         solo.clearEditText(0);  
  77.         solo.enterText(0"Text 1");  
  78.         solo.clickOnMenuItem("Save");  
  79.         solo.assertCurrentActivity("Expected NotesList Activity""NotesList");  
  80.           
  81.         solo.clickLongOnText("中文标签笔记");  
  82.         solo.clickOnText("Delete");  
  83.           
  84.           
  85.     }  
  86. }  
初始化solo的时候和testcase里面都没有去调用getActivity(),但是在testcase开始前先睡眠5秒,如果在这5秒的过程中你手动把notepad给启动起来,那么睡眠时间过后测试会继续正常运行。

刚才stackoverflow上提到的另外一个方法是重写getActivity()这个IntrumentationTestCase2的方法(注意我们所有的robotium测试类都是继承于该class的):
[java] view plaincopyprint?
  1. @Override  
  2.     public MyActivity getActivity() {  
  3.         if (mActivity == null) {  
  4.             Intent intent = new Intent(getInstrumentation().getTargetContext(), MyActivity.class);  
  5.             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  6.             // register activity that need to be monitored.  
  7.             monitor = getInstrumentation().addMonitor(MyActivity.class.getName(), nullfalse);  
  8.             getInstrumentation().getTargetContext().startActivity(intent);  
  9.             mActivity = (MyActivity) getInstrumentation().waitForMonitor(monitor);  
  10.             setActivity(mActivity);  
  11.         }  
  12.         return mActivity;  
  13.     }  
鉴于本人现在只是做前期的可行性研究,够用就好,且周末手头上也没有目标机器在手进行验证,所以有兴趣的朋友就自己去尝试下吧,

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 主板与cpu不兼容怎么办 cpu跟主板不兼容怎么办 软件与电脑不兼容怎么办 win8系统无限重启怎么办 安装微信旧版本登录提示升级怎么办 手机软件与系统不兼容怎么办 下载旧版本微信闪退登陆不了怎么办 企业微信一直登录失败怎么办 360浏览器9.1经常卡死怎么办 手机版爱奇艺看电影屏幕变小怎么办 找不到旧版本米聊怎么办 苹果id被锁了怎么办 新浪微博支付宝解绑失败怎么办 阿里妈妈升级看不到引流人数怎么办 阿里妈妈账号被冻结怎么办 微博昵称到次数怎么办 五星好评之后忘记截图了怎么办 评价后忘了截图怎么办 好评率太低不能买东西了怎么办 淘宝评价被删了怎么办 淘宝店铺有流量没有成交怎么办 淘宝好评被删了怎么办 淘宝评论被系统删除怎么办 淘宝被商家删除评价怎么办 淘宝评价管理商家删除了怎么办 淘宝商家删除评价我该怎么办 我的评价隐藏了怎么办 淘宝把评论删了怎么办 淘宝虚假交易被删除评价怎么办 淘宝好评评错了怎么办 被淘宝骗了好评怎么办 美团好评被删了怎么办 卖家收到好评内容是差评怎么办 淘宝收货电话写错了怎么办 淘宝评价写错了怎么办 饿了么商家差评怎么办 淘宝不给补差价怎么办 淘宝顾客给差评怎么办 淘宝买家账号体检违规怎么办 买家淘宝账户体检中心违规怎么办 淘宝卖家电话骚扰该怎么办