中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Robotium中調用getActivity()方法導致程序掛起的研究淺析

發布時間:2020-07-02 04:21:02 來源:網絡 閱讀:434 作者:zhukev 欄目:移動開發

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測試代碼取代之):
	@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.問題分析

既然是getActvity()方法出現問題,而該方法原有的bug也已經在最新的版本fixed,在google無所獲的情況下也只能剩下分析源碼這條路了。因為是自己剛在backbook上搭建的自動化研究平臺,為了節省時間,當時沒有下載android的相應源碼,只有sdk,所以第一步必須是先在項目中配置使用上android的源碼,其理與配置javadoc相近,請查看本人之前的一篇文章《How to Configure Javadoc for Robotium Library》,這里不做累術。

加入源碼后調試分析,最終程序掛起在android.test.InstrumentationTestCase中的launchActivityWithIntent方法中,以下是eclipse中的調試截圖示例:
Robotium中調用getActivity()方法導致程序掛起的研究淺析
以下是該方法的完整代碼片段:
/**      * 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的,所以不會碰到這個問題。

帶著這個思路在調整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());
因為我們的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給啟動起來,那么睡眠時間過后測試會繼續正常運行。

剛才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;     }
鑒于本人現在只是做前期的可行×××,夠用就好,且周末手頭上也沒有目標機器在手進行驗證,所以有興趣的朋友就自己去嘗試下吧,


 

作者

自主博客

微信

CSDN

天地會珠海分舵

http://techgogogo.com


服務號:TechGoGoGo

掃描碼:

Robotium中調用getActivity()方法導致程序掛起的研究淺析

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

蒙山县| 南阳市| 丘北县| 金山区| 日土县| 绵阳市| 百色市| 江门市| 南昌市| 平利县| 贺州市| 岱山县| 永胜县| 洪洞县| 双江| 永嘉县| 西贡区| 黄山市| 县级市| 烟台市| 邵阳市| 赤壁市| 桂平市| 陇川县| 渭南市| 聂荣县| 同德县| 临潭县| 赤城县| 平乡县| 鲜城| 壶关县| 玛曲县| 炉霍县| 大名县| 察隅县| 汕头市| 方正县| 南江县| 利津县| 弥勒县|