最近2018中文字幕在日韩欧美国产成人片_国产日韩精品一区二区在线_在线观看成年美女黄网色视频_国产精品一区三区五区_国产精彩刺激乱对白_看黄色黄大色黄片免费_人人超碰自拍cao_国产高清av在线_亚洲精品电影av_日韩美女尤物视频网站

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Robotium中調(diào)用getActivity()方法導(dǎo)致程序掛起的研究淺析

1. 問(wèn)題背景描述

在工作中需要在沒(méi)有項(xiàng)目源碼的情況下直接使用robotium測(cè)試目標(biāo)android平臺(tái)launcher,平臺(tái)的版本基于當(dāng)前最新的android 4.4.2。之前在驗(yàn)證可行性的時(shí)候使用本人同樣使用android4.4.2的測(cè)試手機(jī)htc incredable s針對(duì)一個(gè)只有apk的notepad應(yīng)用做過(guò)同樣的驗(yàn)證,在測(cè)試手機(jī)上運(yùn)行完全沒(méi)有問(wèn)題。該測(cè)試代碼如下:

成都創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿(mǎn)足客戶(hù)于互聯(lián)網(wǎng)時(shí)代的河池網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

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; 	//對(duì)應(yīng)re-sign.jar生成出來(lái)的信息框里的兩個(gè)值 	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, "中文標(biāo)簽筆記"); 		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("中文標(biāo)簽筆記"); 		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"); 	} }


但在工作的測(cè)試目標(biāo)平臺(tái)launcher中使用同樣的方法去setup并運(yùn)行簡(jiǎn)單的測(cè)試時(shí)碰到問(wèn)題:測(cè)試程序一直掛起沒(méi)有返回,程序掛起在以下getaActivity()方法(因是公司代碼,故以notepad測(cè)試代碼取代之):
	@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());	 		} 	}

當(dāng)時(shí)一直懷疑是否系統(tǒng)launcher的robotium初始化和setup方法跟普通的apk不一樣,google上有歷史文章描述getActivity()在Android 2.xx.xx上確實(shí)有這個(gè)問(wèn)題,但后來(lái)的版本已經(jīng)解決,而本人使用的時(shí)當(dāng)前最的4.4.2,所以不應(yīng)該還存在這種問(wèn)題。針對(duì)這個(gè)思路去嘗試找解決辦法終無(wú)果。

2.問(wèn)題分析

既然是getActvity()方法出現(xiàn)問(wèn)題,而該方法原有的bug也已經(jīng)在最新的版本fixed,在google無(wú)所獲的情況下也只能剩下分析源碼這條路了。因?yàn)槭亲约簞傇赽ackbook上搭建的自動(dòng)化研究平臺(tái),為了節(jié)省時(shí)間,當(dāng)時(shí)沒(méi)有下載android的相應(yīng)源碼,只有sdk,所以第一步必須是先在項(xiàng)目中配置使用上android的源碼,其理與配置javadoc相近,請(qǐng)查看本人之前的一篇文章《How to Configure Javadoc for Robotium Library》,這里不做累術(shù)。

加入源碼后調(diào)試分析,最終程序掛起在android.test.InstrumentationTestCase中的launchActivityWithIntent方法中,以下是eclipse中的調(diào)試截圖示例:
Robotium中調(diào)用getActivity()方法導(dǎo)致程序掛起的研究淺析
以下是該方法的完整代碼片段:
/**      * 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; }


導(dǎo)致掛起的位置是里面的getInstrumentation().waitForIdleSync()方法,到了這里再代碼跟蹤進(jìn)去看到的就是android.app.instrumentation這個(gè)基類(lèi)里面:
    /**      * 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();     }
這里按照本人的理解做的事情大概如下:
  • 首先確保調(diào)用這個(gè)方法的來(lái)源不是application的主線(xiàn)程
  • 然后把當(dāng)前等待application變成idle的請(qǐng)求放到消息隊(duì)列中
  • 最后等待app在處理完所有事件達(dá)到idle狀態(tài)的時(shí)候返回
看到這里我幡然領(lǐng)悟,在目標(biāo)平臺(tái)上面我們有一個(gè)天氣預(yù)報(bào)的功能,在不停的發(fā)送事件給application(也就是launcher)來(lái)更新當(dāng)前的天氣情況,所以一直沒(méi)有達(dá)到idle的狀態(tài),這樣這個(gè)函數(shù)也就一直沒(méi)有返回而掛起了。而在本人的測(cè)試手機(jī)上測(cè)試的notepad這個(gè)apk,一進(jìn)去的launchable activity就是idle的,所以不會(huì)碰到這個(gè)問(wèn)題。

帶著這個(gè)思路在調(diào)整google關(guān)鍵字在stackoverflow中找到了國(guó)外同行碰到的一個(gè)類(lèi)似的問(wèn)題:http://stackoverflow.com/questions/20860832/why-does-getactivity-block-during-junit-test-when-custom-imageview-calls-start
這里總結(jié)下本人研究過(guò)程中了解到的robotium初始化solo的時(shí)候new Solo(getInstrumentation(),getActivity())中g(shù)etActivity所做的事情:
  • 如果目標(biāo)activity沒(méi)有起來(lái),那么啟動(dòng)該activity并放在前臺(tái)
  • 如果目標(biāo)activity已經(jīng)起來(lái),那么直接放在前臺(tái)等待被測(cè)試
  • 如果該該activity所屬application在自動(dòng)不停的接受事件,直接調(diào)用getActivity會(huì)因?yàn)橐恢钡却齛pplication變成idle狀態(tài)而掛起

3. 解決方法

本人按照項(xiàng)目中的目標(biāo)測(cè)試launcher的實(shí)際情況想到的解決方法是在初始化solo的時(shí)候不去調(diào)用getActivity()這個(gè)InstrumentationTestCase2的方法:
solo = new Solo(getInstrumentation());
因?yàn)槲覀兊膌auncher在robotium在kill掉原來(lái)的launcher進(jìn)程的時(shí)候就會(huì)自動(dòng)起來(lái),所以并不需要手動(dòng)的去getActivity()去啟動(dòng)。這種方法在不能啟動(dòng)起來(lái)的apk如notepad上面就不行,不信你去掉getActivity()的調(diào)用,保證notepad不會(huì)啟動(dòng)或者放到前臺(tái)。但是如果你在開(kāi)始測(cè)試前先把notepad手動(dòng)起來(lái)并放到前臺(tái),測(cè)試還是會(huì)正常進(jìn)行的。比如以下的驗(yàn)證性代碼:
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; 	//對(duì)應(yīng)re-sign.jar生成出來(lái)的信息框里的兩個(gè)值 	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, "中文標(biāo)簽筆記"); 		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("中文標(biāo)簽筆記"); 		solo.clickOnText("Delete"); 		 		 	} }
初始化solo的時(shí)候和testcase里面都沒(méi)有去調(diào)用getActivity(),但是在testcase開(kāi)始前先睡眠5秒,如果在這5秒的過(guò)程中你手動(dòng)把notepad給啟動(dòng)起來(lái),那么睡眠時(shí)間過(guò)后測(cè)試會(huì)繼續(xù)正常運(yùn)行。

剛才stackoverflow上提到的另外一個(gè)方法是重寫(xiě)getActivity()這個(gè)IntrumentationTestCase2的方法(注意我們所有的robotium測(cè)試類(lèi)都是繼承于該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;     }
鑒于本人現(xiàn)在只是做前期的可行×××,夠用就好,且周末手頭上也沒(méi)有目標(biāo)機(jī)器在手進(jìn)行驗(yàn)證,所以有興趣的朋友就自己去嘗試下吧,


       

      作者

      自主博客

      微信

      CSDN

      天地會(huì)珠海分舵

      http://techgogogo.com


      服務(wù)號(hào):TechGoGoGo

      掃描碼:

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

      http://fisionsoft.com.cn/article/jdcgjs.html