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应用做过同样的验证,在测试手机上运行完全没有问题。该测试代码如下:
- 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");
- }
- }
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 {//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测试代码取代之):
- @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());
- }
- }
@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;
- }
/** * 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();
- }
/** * 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状态的时候返回
带着这个思路在调整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的方法:
- solo = new Solo(getInstrumentation());
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");
- }
- }
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的):
- @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;
- }
@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; }鉴于本人现在只是做前期的可行性研究,够用就好,且周末手头上也没有目标机器在手进行验证,所以有兴趣的朋友就自己去尝试下吧,
0 0
- Robtinum的get Activity
- Robtinum
- Robtinum
- Robtinum使用
- Robtinum之instrumention
- get current foreground activity use adb
- Activity的生命周期,activity跳转到activity
- 什么是Activity?Activity的生命周期!
- Activity及Activity的生命周期
- get the return value from the new activity
- Hibernate的get函数
- "post/get"的区别
- Get是不安全的
- Post和Get的
- 安全的GET提交
- 改进的Get-PSDrive
- apt-get 的使用
- js里的Get
- (1) other&interview --> 设计模式:Java开发中的23种设计模式详解(转)
- ThinkPHP----memory
- iOS开发网络篇—HTTP协议
- OC中属性self.a与_a访问的区别
- 函数中的栈内存
- Robtinum的get Activity
- 在Source Insight中看Python代码
- Apache配置多个监听端口和不同的网站目录的简单方法
- linux下yum安装LAMP
- EditText智能搜索点亮ListView中的被查找字符
- linux入门(安装软件)
- 23中设计模式单例模式
- ARM 微处理器的数据类型
- Two Sum | LeetCode(1)