亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

Robotium源碼分析之運行原理

發布時間:2020-06-29 11:30:51 來源:網絡 閱讀:343 作者:zhukev 欄目:移動開發

從上一章《Robotium源碼分析之Instrumentation進階》中我們了解到了Robotium所基于的Instrumentation的一些進階基礎,比如它注入事件的原理等,但Robotium作為一個測試框架,其功能遠不止于只是方便我們注入事件,其應該還包含其他高級的功能,參照我們前面其他框架如MonkeyRunner,UiAutomator和Appium的源碼分析,我們知道一個移動平臺自動化測試框架的基本功能除了事件注入外起碼還應該有控件獲取的功能。所以,這篇文章我們主要是圍繞Robotium的這幾個功能做闡述。

Robotium作為一個自動化測試框架,做一個自動化測試庫,其所要走的事情就是要封裝好獲取控件和操作控件的各種方法,而因為Robotium編寫的腳本又是和目標測試應用運行在同一進程中的(參考《Robotium源碼分析之Instrumentation進階》第一節),所以就會讓事情更簡單了。比如獲取一個TextView的文本,我們就可以直接在測試腳本這個子線程調用目標控件的個相應方法來獲得文本,如果要設置控件的文本屬性的話,我們也只是需要提供一個runnable讓主線程UiThread去調用控件的設置Text屬性的方法就完成了。當然,如果你是想要模擬用戶鍵盤輸入來設置文本,那么就需要操作事件的另外一個方式,去注入事件發送鍵盤事件來完成了。也就是說,你的腳本既可以運用作為UiThread子線程的優勢來直接操控控件屬性,也可以通過注入事件的方式來模擬用戶層面對控件的操作


1.通過注入事件操作控件 

在上一章《Robotium源碼分析之Instrumentation進階》中我們談到了Instrumentation通過InputManager注入事件的幾種方式:

Method

Description

Comment

Key Events

sendKeySync

發送一個鍵盤事件,注意同一時間只有一個action,或者是按下,或者是彈起,所有下面其他key相關的事件注入都是以這個方法為基礎的


sendKeyDownUpSync

基于sendKeySync發送一個按鍵的按下和彈起兩個事件


sendCharacterSync

發送鍵盤上的一個字符,完整的過程包括一個按下和彈起事件


sendStringSync

往應用發送一串字符串


Tackball Event

sendTrackballEventSync

發送軌跡球事件。個人沒有用過,應該是像黑莓的那種軌跡球吧


Pointer Event

sendPointerSync

發送點擊事件


今天我們就通過查看Solo類的clickOnText暴露出來的方法看下最終Robotium是如果通過Instrumentation調用InputManager的sendPointerSync來注入事件的,我們先定位到Solo類的該方法:
/*      */   public void clickOnText(String text) /*      */   { /* 1095 */     this.clicker.clickOnText(text, false, 1, true, 0); /*      */   }
直接跳轉到Clicker類的clickOnText方法,各個參數的意義也一目了然:
/*     */   public void clickOnText(String regex, boolean longClick, int match, boolean scroll, int time) /*     */   { /* 427 */     TextView textToClick = this.waiter.waitForText(regex, match, Timeout.getSmallTimeout(), scroll, true, false); /*     */      /* 429 */     if (textToClick != null) { /* 430 */       clickOnScreen(textToClick, longClick, time); /*     */  /*     */  /*     */  /*     */     } /* 435 */     else if (match > 1) { /* 436 */       Assert.fail(match + " matches of text string: '" + regex + "' are not found!"); /*     */     } /*     */     else /*     */     { /* 440 */       ArrayList<TextView> allTextViews = RobotiumUtils.removeInvisibleViews(this.viewFetcher.getCurrentViews(TextView.class, true)); /* 441 */       allTextViews.addAll(this.webUtils.getTextViewsFromWebView()); /*     */        /* 443 */       for (TextView textView : allTextViews) { /* 444 */         Log.d("Robotium", "'" + regex + "' not found. Have found: '" + textView.getText() + "'"); /*     */       } /* 446 */       allTextViews = null; /* 447 */       Assert.fail("Text string: '" + regex + "' is not found!"); /*     */     } /*     */   }

第一步當然是先獲得控件了,具體怎么獲得往后章節會詳細描述。現在重點看430行clickOnScreen方法,注意參數longCilck代表用戶想注入的點擊方法是長按還是短按:

/*     */   public void clickOnScreen(View view, boolean longClick, int time) /*     */   { /* 182 */     if (view == null) { /* 183 */       Assert.fail("View is null and can therefore not be clicked!"); /*     */     } /* 185 */     float[] xyToClick = getClickCoordinates(view); /* 186 */     float x = xyToClick[0]; /* 187 */     float y = xyToClick[1]; /*     */      /* 189 */     if ((x == 0.0F) || (y == 0.0F)) { /* 190 */       this.sleeper.sleepMini(); /*     */       try { /* 192 */         view = this.viewFetcher.getIdenticalView(view); /*     */       } /*     */       catch (Exception ignored) {} /* 195 */       if (view != null) { /* 196 */         xyToClick = getClickCoordinates(view); /* 197 */         x = xyToClick[0]; /* 198 */         y = xyToClick[1]; /*     */       } /*     */     } /*     */      /* 202 */     if (longClick) { /* 203 */       clickLongOnScreen(x, y, time, view); /*     */     } else { /* 205 */       clickOnScreen(x, y, view); /*     */     } /*     */   }
先根據控件獲得控件點擊坐標:其實就是控件的中心點的絕對坐標值了,該轉換在getClickCoordinates方法進行,沒有什么特別的地方,就不跳進去分析了。

然后根據是否是長按考慮調用clickLongOnScreen或者clickOnScreen方法,我們這里挑clicOnScreen往下展開:

/*     */   public void clickOnScreen(float x, float y, View view) /*     */   { /*  77 */     boolean successfull = false; /*  78 */     int retry = 0; /*  79 */     SecurityException ex = null; /*     */      /*  81 */     while ((!successfull) && (retry < 10)) { /*  82 */       long downTime = SystemClock.uptimeMillis(); /*  83 */       long eventTime = SystemClock.uptimeMillis(); /*  84 */       MotionEvent event = MotionEvent.obtain(downTime, eventTime, 0, x, y, 0); /*     */        /*  86 */       MotionEvent event2 = MotionEvent.obtain(downTime, eventTime, 1, x, y, 0); /*     */       try /*     */       { /*  89 */         this.inst.sendPointerSync(event); /*  90 */         this.inst.sendPointerSync(event2); /*  91 */         successfull = true; /*     */       } catch (SecurityException e) { /*  93 */         ex = e; /*  94 */         this.dialogUtils.hideSoftKeyboard(null, false, true); /*  95 */         this.sleeper.sleep(200); /*  96 */         retry++; /*  97 */         View identicalView = this.viewFetcher.getIdenticalView(view); /*  98 */         if (identicalView != null) { /*  99 */           float[] xyToClick = getClickCoordinates(identicalView); /* 100 */           x = xyToClick[0]; /* 101 */           y = xyToClick[1]; /*     */         } /*     */       } /*     */     } /* 105 */     if (!successfull) { /* 106 */       Assert.fail("Click at (" + x + ", " + y + ") can not be completed! (" + (ex != null ? ex.getClass().getName() + ": " + ex.getMessage() : "null") + ")"); /*     */     } /*     */   }
所做的事情就是根據點擊坐標組建兩個分別按下和彈起的事件,然后在89和90行分別調用Instrumentation的sendPointerSync方法觸發InputManager注入這兩個按下和彈起的事件就完了。有了以前文章的基礎,這些代碼分析起來就很流暢了,不然到了這里還要去跟大家解析各種事件注入的情況就會顯得很冗長乏味了。

2. 通過runOnMainSync操作控件

Robotium除了可以通過InputManager注入事件的方式來操作控件的點擊輸入等之外,還可以通過在主線程直接運行修改控件屬性的代碼來操作控件,比如修改一個TextView控件的文本,我們通過上一章的《Robotium源碼分析之Instrumentation進階》知道在主線程控制控件有兩種方法,一種runOnMainSync的同步運行方式,一種runOnUiThread的異步運行方式,這里我們先看第一種的例子,Solo類的enterText方法:
/*      */   public void enterText(EditText editText, String text) /*      */   { /* 1748 */     editText = (EditText)this.waiter.waitForView(editText, Timeout.getSmallTimeout()); /* 1749 */     this.textEnterer.setEditText(editText, text); /*      */   }
首先還是如上獲得控件,然后調用TextEnterer類的setEditText方法:
/*    */   public void setEditText(final EditText editText, final String text) /*    */   { /* 45 */     if (editText != null) { /* 46 */       final String previousText = editText.getText().toString(); /*    */        /* 48 */       this.inst.runOnMainSync(new Runnable() /*    */       { /*    */         public void run() /*    */         { /* 52 */           editText.setInputType(0); /* 53 */           editText.performClick(); /* 54 */           TextEnterer.this.dialogUtils.hideSoftKeyboard(editText, false, false); /* 55 */           if (text.equals("")) { /* 56 */             editText.setText(text); /*    */           } else { /* 58 */             editText.setText(previousText + text); /* 59 */             editText.setCursorVisible(false); /*    */           } /*    */         } /*    */       }); /*    */     } /*    */   }
毫無意外的這個方法在48行開始調用的就是runOnMainSync的方法來給主線程執行,所做的事情大致如下:
  • 設置控件的輸入類型,比如如果是密碼輸入框的話就不要顯示輸入過的字符,以*號代替等
  • 模擬點擊設置焦點到目標輸入控件
  • 因為是直接對控件設置,所以不需要通過鍵盤驅動,所以也就沒有必要顯示鍵盤出來了
  • 直接設置控件的Text

3.通過runOnUiThread操作控件

看完了同步設置EditText文本屬性Solo的enterText方法的例子,那么我們繼續往下看下異步的直接在主線程設置控件屬性的一個例子,我們這里挑的是Solo的setProgressBar這個方法:
/*      */   public void setProgressBar(ProgressBar progressBar, int progress) /*      */   { /* 1691 */     progressBar = (ProgressBar)this.waiter.waitForView(progressBar, Timeout.getSmallTimeout()); /* 1692 */     this.setter.setProgressBar(progressBar, progress); /*      */   
直接跳入到Setter類的setProgressBar方法:
/*     */   public void setProgressBar(final ProgressBar progressBar, final int progress) /*     */   { /* 101 */     if (progressBar != null) /*     */     { /* 103 */       this.activityUtils.getCurrentActivity(false).runOnUiThread(new Runnable() /*     */       { /*     */         public void run() /*     */         { /*     */           try { /* 108 */             progressBar.setProgress(progress); /*     */           } /*     */           catch (Exception ignored) {} /*     */         } /*     */       }); /*     */     } /*     */   }
103行可以看到是使用了runOnUiThread的方法在主線程直接提交修改控件屬性的消息然后放到UiThread MainLooper來排隊修改進度條屬性的。
迄今為止我們看到到了以下幾種控制控件的方法:
  • 通過Instrumentation調用InputManager注入事件
  • 通過runOnMainSync在主線程同步直接修改控件屬性
  • 通過runOnUiThread在主線程異步修改控件屬性
至于后兩者我們只知道一個是同步的一個是異步的,但是為什么在例子中設置TextView的Text屬性需要的就是同步的,而在設置ProgressBar的Progress需要的就是異步的呢?這里本人并沒有很好的解析為Robotium是根據什么策略決定的,希望有知道的朋友評論一下,以防有朋友如我般容易鉆牛角尖的同學可以釋疑。

4. Robotium跨應用

Robotium可以通過Android4.3之后Instrumentation引入得getUiAutomation接口獲得UiAutomation得實例進行跨應用測試,具體用戶腳本實現留給大家練習,因為整個系列本人描述的都不是實戰經驗了,至于具體怎么個回事請查看《Robotium源碼分析之Instrumentation進階》第7節。

5. 通過WindowManager獲取控件

本來打算像往常一樣按照自己的邏輯重新分析Robotium獲取控件的原理的,但發現網上已有先驅撰文《Robotium 5.0.1 源碼解析之控件搜索》做了相應的分析了,且近來身體欠佳,牙痛,頭痛,發燒干嘛來襲的,所以就干脆直接引用了,本人覺得寫的還ok,大家閱讀應該不會存在問題的了,這里就先謝過作者了。其實獲取一個控件的方法無非是先獲取得ui界面上得根控件,然后從根控件開始搜索下面指定的控件,在UiAutomator中我們用AccessibilityNodeInfo來封裝一個view,而在Robotium中我們還是使用view和ViewGroup本身而已,注意這里View是描述一個控件的最小單位,而ViewGroup是view的容器,比如最上層的DecorView就是包含了界面所有控件的容器,所以獲得這個容器就能獲得所有的子控件。再次說明,以下分析是摘錄自網上的,本人只做了排版調整

     眾所周知,Robotium是基于Android的單元測試框架Instrumentation,而robotium對于Instrumentation封裝的比較強的地方便是控件搜索,這部分的源碼主要位于ViewFetcher.java中。     

5.1  mViews的獲取

    要先搜索控件,必須先得到Activity的rootView。在Android中,對于一般的Activity或其對話框,其rootView叫做DecorView,其實就是Activity和Dialog外面的那層框(關于Activity或dialog的層次可以用HierarchyViewer來查看)。

   雖然通過Activity類的getWindow().getDecorView可以獲取到Activity自身的DecorView,但是無法獲取到對話框的,因此Robotium中界面控件是從WindowManagerGlobal(或WindowManagerImpl)中的mViews獲取到的。當然mViews中不但包含DecorView,還包含同進程內的所有界面的根節(如懸浮框的根節點)。mView的值的獲取過程主要如下: 

   1) 確定mViews所在類:android 4.2之前,獲取類為android.view.WindowManagerImpl,4.2及之后,獲取類為WindowManagerGlobal

 String windowManagerClassName;    if (android.os.Build.VERSION.SDK_INT >= 17) {           windowManagerClassName = "android.view.WindowManagerGlobal";    } else {           windowManagerClassName = "android.view.WindowManagerImpl";    }   windowManager = Class.forName(windowManagerClassName)  <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">    </span>

    2). 獲得類的實例:此類是個單例類,有直接的靜態變量可以獲取到其實例, 4.2及之后的版本其變量名為sDefaultWindowManager,3.2至4.1,其變量名為sWindowManager,3.2之前,其變量名為mWindowManager。

/**   * Sets the window manager string.   */   private void setWindowManagerString(){               if (android.os.Build.VERSION.SDK_INT >= 17) {                     windowManagerString = "sDefaultWindowManager";            } else if(android.os.Build.VERSION.SDK_INT >= 13) {                     windowManagerString = "sWindowManager";            } else {                     windowManagerString = "mWindowManager";            }   }  

    3). 獲取mViews變量的值了,從4.4開始類型變為ArrayList<View>,之前為View[]

viewsField = windowManager.getDeclaredField("mViews");   instanceField = windowManager.getDeclaredField(windowManagerString);   viewsField.setAccessible(true);   instanceField.setAccessible(true);   Object instance = instanceField.get(null);   View[] result;   if (android.os.Build.VERSION.SDK_INT >= 19) {             result = ((ArrayList<View>) viewsField.get(instance)).toArray(new View[0]);   } else {             result = (View[]) viewsField.get(instance);   }  

5.2 mViews的過濾

   mViews中會包含三種類型的View:

   1) 當前顯示的以及沒有顯示的Activity的DecorView

   2) 當前對話框的DecorView

   3) 懸浮框View等其他不屬于DecorView的獨立View 

     在搜索控件時,顯然需要在最上層界面中搜索,所以搜索范圍為: 

              最上層的Activity/Dialog + 懸浮框

     對于懸浮框,robotium中的處理是找出mViews中不屬于DecorView類的View,并將其所有子控件引入。

private final View[] getNonDecorViews(View[] views) {            View[] decorViews = null;               if(views != null) {                decorViews = new View[views.length];                   int i = 0;                View view;                   for (int j = 0; j < views.length; j++) {                    view = views[j];                    if (view != null && !(view.getClass().getName()                            .equals("com.android.internal.policy.impl.PhoneWindow$DecorView"))) {                        decorViews[i] = view;                        i++;                    }                }            }            return decorViews;        }  

    對于Activity/Dialog的篩選,Robotium采取對比DrawingTime的方法選出最后繪制的DecorView,其即為最上層Activity/Dialog的DecorView:

/**       * Returns the most recent view container       *       * @param views the views to check       * @return the most recent view container       */           private final View getRecentContainer(View[] views) {            View container = null;            long drawingTime = 0;            View view;               for(int i = 0; i < views.length; i++){                view = views[i];                if (view != null && view.isShown() && view.hasWindowFocus() && view.getDrawingTime() > drawingTime) {                    container = view;                    drawingTime = view.getDrawingTime();                }            }            return container;        }  

5.3 控件過濾&控件列表生成

     得到懸浮框的根節點和最上層的DecorView后,robotium會將所有View統一添加到一個ArrayList中生成控件列表。添加方法本身很簡單,就是一個簡單的遞歸,但需要注意的是此處有一個onlySufficientlyVisible的判斷。onlySufficientlyVisible是ViewFetcher中最常見的一個變量,其表示是否過濾掉顯示不完全的控件,即onlySufficientlyVisible為true時表示只在顯示完全的控件中搜索目標,為false時表示在所有控件中搜索目標。具體代碼為下面的addChildren函數:

private void addChildren(ArrayList<View> views, ViewGroup viewGroup, boolean onlySufficientlyVisible) {           if(viewGroup != null){               for (int i = 0; i < viewGroup.getChildCount(); i++) {                   final View child = viewGroup.getChildAt(i);                      if(onlySufficientlyVisible && isViewSufficientlyShown(child))                       views.add(child);                      else if(!onlySufficientlyVisible)                       views.add(child);                      if (child instanceof ViewGroup) {                       addChildren(views, (ViewGroup) child, onlySufficientlyVisible);                   }               }           }       }  

從上面的代碼可以看出,當onlySufficientlyVisible為true時,robotium會對控件的可見不可見進行檢查。不過這里的可見不可見不是指Visible或Invisible(Robotium過濾Invisible控件的方法是RobotiumUtils.removeInvisibleViews,原理是利用view.isShown()方法),而是指由于界面滾動而導致的沒有顯示或顯示不完全。繼續看Robotium對SufficientlyVisible是怎么判斷的:

public final boolean isViewSufficientlyShown(View view){           final int[] xyView = new int[2];           final int[] xyParent = new int[2];              if(view == null)               return false;              final float viewHeight = view.getHeight();           final View parent = getScrollOrListParent(view);           view.getLocationOnScreen(xyView);              if(parent == null){               xyParent[1] = 0;           }           else{               parent.getLocationOnScreen(xyParent);           }              if(xyView[1] + (viewHeight/2.0f) > getScrollListWindowHeight(view))               return false;              else if(xyView[1] + (viewHeight/2.0f) < xyParent[1])               return false;              return true;       }  

    代碼中getScrollOrListParent是獲取控件所屬的ListView或ScrollView,可能是控件本身也可能是空。getScrollListWindowHeight函數用于獲取控件所屬的ListView或ScrollView最下面邊界的Y坐標。因此
xyView[1] + (viewHeight/2.0f) > getScrollListWindowHeight(view)  
這個判斷就表示控件有超過一半的面積被隱藏在了父控件的下方,而
(xyView[1] + (viewHeight/2.0f) < xyParent[1]  

則表示控件有超過一半的面積被隱藏在了父控件的上方,這兩種情況都被Robotium判斷為不滿足SufficientlyVisible的(不過好像沒有判斷橫向的?)。

 根據onlySufficientlyVisible過濾掉相應控件后,robotium便完成了控件列表的生成工作,之后的搜索就可直接在列表中進行查找了。

 有的時候要搜索指定類型的控件,可以按照類型對控件列表進行再一次的過濾,ViewFetcher中的代碼如下:

public <T extends View> ArrayList<T> getCurrentViews(Class<T> classToFilterBy, View parent) {             ArrayList<T> filteredViews = new ArrayList<T>();             List<View> allViews = getViews(parent, true);             for(View view : allViews){                    if (view != null && classToFilterBy.isAssignableFrom(view.getClass())) {                           filteredViews.add(classToFilterBy.cast(view));                    }             }             allViews = null;             return filteredViews;      }  

可以看到,robotium直接利用了Class. isAssignableFrom進行類型的匹配。

 

5. 4.文本搜索

獲得了控件列表,可以開始搜索指定的目標控件了,先從我們最常用的文本搜索開始,看看robotium的搜索流程。搜索過程的代碼主要位于Searcher.java中,主要功能在兩個searchFor函數中實現,通過嵌套完成目標的搜索。

第一層

<strong>    public <T extends TextView> T searchFor(final Class<T> viewClass, final String regex, int expectedMinimumNumberOfMatches, final long timeout, final boolean scroll, final boolean onlyVisible) {           //修正非法的expectedMinimumNumberOfMatches           if(expectedMinimumNumberOfMatches < 1) {               expectedMinimumNumberOfMatches = 1;           }              //定義一個Callable給下層searchFor使用,可以直接獲取到符合條件的控件列表           final Callable<Collection<T>> viewFetcherCallback = new Callable<Collection<T>>() {               @SuppressWarnings("unchecked")               public Collection<T> call() throws Exception {                   sleeper.sleep();                   //從當前的Android View中獲取到符合viewClass的控件列表                   ArrayList<T> viewsToReturn = viewFetcher.getCurrentViews(viewClass);                                         if(onlyVisible){                       //過濾掉Invisible的控件                       viewsToReturn = RobotiumUtils.removeInvisibleViews(viewsToReturn);                   }                               //robotium支持在webView中查找網頁控件,因此若目標控件是TextView或是TextView的子類,                   //會把網頁中的文本框也加到控件列表中。                   if(viewClass.isAssignableFrom(TextView.class)) {                       viewsToReturn.addAll((Collection<? extends T>) webUtils.getTextViewsFromWebView());                   }                   return viewsToReturn;               }           };              try {               //調用下層searchFor繼續搜索               return searchFor(viewFetcherCallback, regex, expectedMinimumNumberOfMatches, timeout, scroll);           } catch (Exception e) {               throw new RuntimeException(e);           }       }   </strong>  

   這個函數的主要功能有二,一是對非法的expectedMinimumNumberOfMatches進行修正,二是為下一層searchFor提供一個Callable,里面定義好了控件列表的獲取過程。

   1)      expectedMinimumNumberOfMatches:這個參數表示搜索目標最小發現數目,當一個界面中有多個控件滿足搜索條件,通過此參數可以指定想要獲取的是第幾個。

  2)      Callable<Collection<T>> viewFetcherCallback:定義了控件列表(即搜索范圍)的獲取過程。首先利用前面提到的viewFetcher.getCurrentViews(viewClass)獲取一個初步的列表;再通過RobotiumUtils.removeInvisibleViews(viewsToReturn)過濾掉不可見控件;最后由于Robotium支持webView內部搜索(Robotium的名字貌似也是來源于Selenium),所以當搜索目標是一個TextView時,Robotium還會調用webUtils.getTextViewsFromWebView()把網頁中的文本框加入到搜索范圍中。

 

第二層

<strong>    public <T extends TextView> T searchFor(Callable<Collection<T>> viewFetcherCallback, String regex, int expectedMinimumNumberOfMatches, long timeout, boolean scroll) throws Exception {           final long endTime = SystemClock.uptimeMillis() + timeout;             Collection<T> views;              while (true) {                              final boolean timedOut = timeout > 0 && SystemClock.uptimeMillis() > endTime;                  if(timedOut){                   logMatchesFound(regex);                   return null;               }                   //獲取符合條件的控件列表               views = viewFetcherCallback.call();                  for(T view : views){                   if (RobotiumUtils.getNumberOfMatches(regex, view, uniqueTextViews) == expectedMinimumNumberOfMatches) {                       uniqueTextViews.clear();                       return view;                   }               }               if(scroll && !scroller.scrollDown()){                   logMatchesFound(regex);                   return null;                }               if(!scroll){                   logMatchesFound(regex);                   return null;                }           }       }</strong>  

    這一層的主要功能就是循環在控件列表中找到含有指定文本的控件,直至超時或發現了   expectedMinimumNumberOfMatches數目的目標控件,這個過程中需要注意的有四點:

   1)    uniqueTextViews:為了防止找到的控件存在重復,此處用了一個uniqueTextViews集合來存儲搜索到的結果。

   2)    文本的匹配:直接利用了Pattern進行正則匹配,但比對的內容不只包括view.getText(),還包括 view.getError()以及view.getHint()

   3)    自動滾動:當開啟了scroll選項,并且在當前的界面沒有找到足夠的目標時,Robotium會自動滾動界面 (不過好像只會向下?):

if(scroll && !scroller.scrollDown()  

   4)     滾動時robotium只會滾動drawingTime最大的控件(通過ViewFetcher.getFreshestView()),所以一個界面中有兩個可滾動控件時,robotium只會滾動其中一個。


6.引用

第四節引用出處:Robotium 5.0.1 源碼解析之控件搜索》,這里同時對作者表達本人感謝之情,省了我去重新分析的時間!


 

作者

自主博客

微信

CSDN

天地會珠海分舵

http://techgogogo.com


服務號:TechGoGoGo

掃描碼:

Robotium源碼分析之運行原理

向AI問一下細節

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

AI

滁州市| 平遥县| 张家界市| 高邮市| 大连市| 安平县| 英超| 增城市| 阿荣旗| 乌鲁木齐县| 嘉鱼县| 宁城县| 泰宁县| 岱山县| 三穗县| 玛曲县| 内江市| 江达县| 大姚县| 仪陇县| 遂溪县| 金乡县| 庆元县| 长阳| 万州区| 达州市| 枣强县| 阿克陶县| 南召县| 宁河县| 汝州市| 富平县| 昆山市| 绥阳县| 潞西市| 永清县| 东安县| 罗平县| 禄丰县| 凤山市| 固原市|