Analysis on program suspension caused by calling getActivity () method in Robotium, getactivity
1. Problem background description
In work, you need to directly use robotium to test the target android platform launcher without the project source code. The platform version is based on the latest android 4.4.2. Previously, I used my android 4.4.2 Test mobile phone htc incredable s to perform the same verification for a notepad app with only apk, and it was no problem to run it on the test mobile phone. The test code is as follows:
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 I Nt run = 0; private static Class <?> LaunchActivityClass; // corresponds to the two values in the information box generated by the 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 wocould be re-instantiated // which wowould lead to soto 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, "Chinese tag 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 ("Chinese tag note"); 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 ");}}
However, if you use the same method in the launcher of the testing target platform to setup and run a simple test, you will encounter a problem: the test program has been suspended and no return is returned, and the program is suspended in the following getaActivity () method (replaced by the notepad test code because it is a company code ):
@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());}}
At that time, I had been wondering whether the system launcher's robotium initialization and setup methods were different from the common apk methods. google has a historical article describing getActivity () on Android 2. xx. this problem exists on xx, but later versions have been solved, and I am using the most 4.4.2, so this problem should not exist. There is no result in trying to find a solution based on this idea.
2. the problem analysis is that the getActvity () method has a problem, and the original bug of this method has been fixed in the latest version, in the case that google has nothing to gain, there is only the path to source code analysis. Because it was an automated research platform that I just set up on the backbook. In order to save time, the corresponding android source code was not downloaded at the time. Only the sdk was available, therefore, the first step must be to configure and use the android source code in the project, which is similar to configuring javadoc, please refer to my previous article "How to Configure Javadoc for Robotium Library". I will not be tired here.
After the source code is added for debugging and analysis, the program is suspended in the launchActivityWithIntent method in android. test. InstrumentationTestCase. The following is an example of debugging in eclipse:
The complete code snippet for this method is as follows:
/** * 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; }
The suspended location is the getInstrumentation (). waitForIdleSync () method in it. Here, the code is tracked to see the base class 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(); }
Here, I will do the following according to my understanding:
- First, make sure that the source of this method is not the main thread of the application.
- Then, put the request waiting for the application to become idle in the message queue.
- Finally, when the app finishes processing all events and reaches the idle status, it will return
Here I suddenly realized that we have a weather forecast function on the target platform, and we are constantly sending events to application (that is, launcher) to update the current weather conditions, so the idle state has not been reached, so this function has not been returned and suspended. The notepad apk tested on my mobile phone is an idle of the launchable activity, so this problem is not encountered.
With this idea, we found a similar problem in stackoverflow when adjusting google keywords: getActivity in Solo (getInstrumentation (), getActivity:
- If the target activity does not exist, start the activity and put it on the foreground
- If the target activity has been started, it will be placed on the foreground and waiting for testing.
- If the application to which the activity belongs automatically and continuously accepts the event, directly calling getActivity will be suspended because the application remains in the idle state.
3. solution I think of the solution based on the actual situation of the launcher test in the project. The solution is not to call the method of getActivity () InstrumentationTestCase2 when initializing solo:
solo = new Solo(getInstrumentation());
Because our launcher will automatically get up when the original launcher process is killed in robotium, it does not need to be manually started by getActivity. This method does not work on an apk that cannot be started, such as notepad. If you do not believe that you have removed the call to getActivity (), ensure that notepad will not be started or put on the frontend. However, if you manually add the notepad to the front-end before starting the test, the test will continue normally. For example, the following validation code:
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 I Nt run = 0; private static Class <?> LaunchActivityClass; // corresponds to the two values in the information box generated by the 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 wocould be re-instantiated // which wowould lead to soto 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, "Chinese tag 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 ("Chinese tag note"); solo. clickOnText ("Delete ");}}
GetActivity () is not called in solo initialization and testcase, but sleep for 5 seconds before testcase starts, if you manually start notepad during the five seconds, the test will continue to run normally after the sleep time.
Another method mentioned in stackoverflow just now is to override the IntrumentationTestCase2 method of getActivity () (note that all our robotium testing classes inherit from this 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; }
In view of the fact that I am only doing the feasibility study in the early stage, it is enough, and there is no target machine on hand for verification on the weekend, so if you are interested, try it on your own,
How to test pre-installed apk in robotium
Robotium tests the Android apk installation package
Introduction
To test the apk program, you must have the same signature as the test program we wrote ). If the apk program's signature key is not available, you need to remove the apk program's signature and then use your own key to sign it (in this step, we can use the debug key ), there is already a ready-made tool available, re-sign.jar, which can remove the original signature of the apk program, and then use our own debug key to sign it.
Details
Before writing test cases, we need to know the package name and launch page name of the apk program ), the two names can be obtained through the log (adb logcat) at program startup. The program log prints the package name and activity name. The log format is similar to Starting activity: Intent {act = android. intent. action. MAIN cat = android. intent. category. LAUNCHER? Flg = 0x10200000 cmp = com. example. android. notepad/. NotesList.
The preceding logs indicate that the package name is com. example. android. notepad, And the startup activity name is com. example. android. notepad. NotesList. The activity name is LAUNCHER_ACTIVITYFULLCLASSNAME in the following code.
The following is an example of the test code:
Package com. yourcompany. yourtestname;
Import com. jayway. android. robotium. solo. Solo;
Import android. test. ActivityInstrumentationTestCase2;
@ SuppressWarnings ("rawtypes ")
Public class ReallyBlackboxTest extends ActivityInstrumentationTestCase2 {
Private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "com. newsrob. DashboardListActivity ";
Private static Class <?> LauncherActivityClass;
Static {
Try {
LauncherActivityClass = Class. forName (LAUNCHER_ACTIVITY_FULL_CLASSNAME );
} Catch (ClassNotFoundException e ){
Throw new RuntimeException (e );
}
}
@ SuppressWarnings ("unchecked ")
Public ReallyBlackboxTest () throws ClassNotFoundException {
Super (launcherActivityClass );
}
Pri ...... remaining full text>