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

但在工作的測试目标平台launcher中使用相同的方法去setup并执行简单的測试时碰到问题:測试程序一直挂起没有返回,程序挂起在下面getaActivity()方法(因是公司代码。故以notepad測试代码代替之):
    @Overridepublic 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 staticif(solo == null) {     NotePadTest.solo = new Solo(getInstrumentation(),getActivity());   }}

当时一直怀疑是否系统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中的调试截图演示样例:
下面是该方法的完整代码片段:
/*** Utility method for launching an activity with a specific Intent.* * <p><b>NOTE:</b> The parameter <i>pkg</i> 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 extends Activity> T launchActivityWithIntent(String pkg,Class<T> 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状态的时候返回

看到这里我幡然领悟。在目标平台上面我们有一个天气预报的功能。在不停的发送事件给application(也就是launcher)来更新当前的天气情况。所以一直没有达到idle的状态。这样这个函数也就一直没有返回而挂起了。而在本人的測试手机上測试的notepad这个apk,一进去的launchable activity就是idle的。所以不会碰到这个问题。

带着这个思路在调整googlekeyword在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的方法:
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);}@Overridepublic 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());}}@Overridepublic 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给启动起来,那么睡眠时间过后測试会继续正常执行。

刚才stackoverflow上提到的另外一个方法是重写getActivity()这个IntrumentationTestCase2的方法(注意我们全部的robotium測试类都是继承于该class的):
@Overridepublic 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://blog.csdn.net/zhubaitian

版权声明:本文博客原创文章,博客,未经同意,不得转载。

Robotium调用getActivity()导致程序挂起的方法相关推荐

  1. 解引用NULL为什么会导致程序挂死?

    来源:公众号[编程珠玑] 作者:守望先生 ID:shouwangxiansheng 解引用NULL指针为什么会出错,导致程序挂死?或者说访问内存地址为0的位置为什么会视为非法? 先了解NULL 参考& ...

  2. php调用sqlldr失败,php system()命令调用sqlplus导致php挂起

    我有一个简单的PHP脚本,它调用sqlplus sqlldr文件并将数据转储到oracle中的表中 . 我把问题缩小到我认为是我的PHP脚本挂起的原因...这是:当我从php CLI运行时,系统命令不 ...

  3. jstack命令案例分析:对wait的线程没有调用notify()导致程序无法停止

    对wait的线程没有调用notify()导致的结果就是程序无法停止. o.notify(); //必须,否则无法停止程序 public class T06_00_sync_wait_notify {p ...

  4. 没想到,错误的单例写法,让 RabbitMQ 大量超时导致程序挂死!

    一:背景 1. 讲故事 10月份星球里的一位老朋友找到我,说他们公司的程序在一个网红直播带货下给弄得无响应了,无响应期间有大量的 RabbitMQ 超时,寻求如何找到根源,聊天截图我就不发了. 既然无 ...

  5. vivado sdk中xil_out函数对指定的BRAM地址写数据为什么会导致程序挂起?

    是有关zynq芯片的网口程序调试的,我在例程lwip_echo_server程序中加入了往指定的bram地址写数据的语句,想要实现PS到PL数据的交互,但是程序执行到xil_out这块直接就运行不下去 ...

  6. c语言调用cmd隐藏黑窗口,golang 调用cmd下程序隐藏黑窗口-方法1

    通过go的标准库exec调用cmd命令时会闪弹黑窗口,为解决此问题在windows下可以用win32 API 的 WinExec. 此问题主要出现在带UI或无控制台的程序调用cmd时. 编译go时加入 ...

  7. JAVA调用C语言程序

    JAVA调用C语言程序 JAVA调用C语言程序 1. 编写带有native声明的方法的Java类 2. 使用javah 生成:jniSample.h的头文件 3. 使用C实现本地sum方法:(这里我生 ...

  8. 网页Web上调用本地应用程序(.exe)

     一.web调用本地应用程序的思路 这个方法主要思路是利用自定义URL Protocol来调用应用程序.浏览器在解析到自定义URL Protocol之后,会寻找注册表,然后通过注册表启动相应的程序 ...

  9. 托管调试助手“LoaderLock”在XXX中检测到故障。其他信息:正尝试在OS加载程序锁内执行托管代码。不要尝试在DllMain或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。

    在程序中执行new一个对象A时,出现了错误信息: 托管调试助手"LoaderLock"在XXX中检测到故障.其他信息:正尝试在OS加载程序锁内执行托管代码.不要尝试在DllMain ...

最新文章

  1. 一篇文章了解生物特征识别六大技术
  2. 吐血推荐:win下如何安装tensorflow?只需两步!!
  3. python怎么导入时间-Python的import导入与时间
  4. 净核心vs节点js您应该选择什么
  5. boost::function_types::is_callable_builtin用法的测试程序
  6. requests.get(url)执行没反应_翟至宇:执行路上,从未停歇
  7. 如何确定python开发环境已经配置好_python学习第一天:window安装python开发环境完整篇...
  8. Taro+react开发(51) 数组对象和数组得处理
  9. HDU 1231 最大连续子序列 (动态规划)
  10. 广东科技学院计算机原理组成,201120122操作系统原理期中试卷edited广东科技学院付博士(4页)-原创力文档...
  11. android蓝牙 助手源码,蓝牙串口助手(Android Studio源码)
  12. 4.1 手工编写第一个Jmeter性能测试脚本
  13. ylbtech-dbs:ylbtech-4,PurpleHouse(房地产楼盘销售系统)
  14. 初探PHP开源采集器----蓝天采集器
  15. 一次偶然的CobaltStrike木马钓鱼邮件分析
  16. 有关c基础指针需要注意的几个点!
  17. CentOS7 阻止笔记本合盖时休眠
  18. 一文让你搞懂Mysql中 concat函数,ifnull函数,distinct,+号的使用
  19. linux的Java配置
  20. 逛VeryCD 无意间看到来自JavaAmg77的总结性忠告 和大家分享一下

热门文章

  1. 关于 top、left 结合 translate 实现居中的原理探讨
  2. volatile非原子性示例
  3. C#和Javascript间互转的Xxtea加解密
  4. 使用JMX监控Kafka
  5. 第二单元linux系统
  6. shell 脚本 文件夹扫描获取文件
  7. [Android ] linux命令英文缩写的含义(方便记忆)
  8. #Sora#OpenStack基础库oslo.config试用总结
  9. Bootstrap3.1开发的响应式个人简历模板
  10. 今天的工作发现了4年前的“bug一枚”