Appium基礎學習之 | Instrumentation再續

在《Appium基礎學習之 | UiAutomator2.0使用》最後留下了三個問題

1.什麼叫做運行器,比如AndroidJUnitRunner、instrumentationTestRunner?

2.UiAutomator2.0基於Instrumentation運行,好像非常複雜,這樣做相對於UiAutomator1.0來說有什麼優勢呢?

3.adb shell am instrument命令的解析
從《Android基礎知識學習-Instrumentation啓動源碼簡析》中分析來看,在通過命令adb shell am instrument指定運行器時會取代默認Instrumentation來執行測試,從源碼分析來看,默認系統的Instrumentation類中onCreate是一個空方法,而繼承Instrumentation的AndroidJUnitRunner類重寫了onCreate對將測試的數據做了一些處理,有興趣的可以繼續往下研究下源碼看看AndroidJUnitRunner是如何運行並處理數據的。

一、AndroidJUnitRunner、instrumentationTestRunner

Instrumentation這個測試框架的運行器AndroidJUnitRunner與instrumentationTestRunner在執行測試是基於Junit的(注意是執行測試的類繼承Junit的TestCase類,而不是說Instrumentation繼承Junit

1.AndroidJUnitRunner與instrumentationTestRunner的區別

(1)instrumentationTestRunner運行器是一箇舊的運行器,它只支持Junit3,所以如果使用這個運行器,測試方法必須以test開頭。

(2)AndroidJUnitRunner運行器是Google推薦使用的,它的出現就是替換instrumentationTestRunner的,它支持所有instrumentationTestRunner的特性以及命令格式,並且有一些新的擴展。

二、Instrumentation

在《Android基礎知識學習-Instrumentation啓動源碼簡析》大致簡述了Instrumentation的創建過程,Application對象是通過Instrumentation調用callApplicationOnCreate來完成Application的啓動;而另外很重要的Activity的onCreate在文中並沒有進行源碼分析,但也有提到過,在整個Application初始化完成後,會回到ActivityManagerService的attachApplicationLocked方法中通過調用ActivityStackSupervisor的attachApplicationLocked()方法,最後是通過Instrumentation的newActivity、callActivityOnCreate初始化啓動主Activity的(這個過程的源碼分析就省略了)。然後會調用Activity的attach方法,這個調用是在ActivityThead的performLaunchActivity方法中執行的。

public Instrumentation getInstrumentation()
{
        return mInstrumentation;
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.voiceInteractor);             
    }

調用這個attach入參的參數中關注getInstrumentation()這個方法,它返回的是mInstrumentation,這個值是在調用ActivityThread的handleBindApplication中初始化的。然後來到Activity的attach方法

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
    private static final String TAG = "Activity";
 
    private Instrumentation mInstrumentation;

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, IVoiceInteractor voiceInteractor) {

......
        mInstrumentation = instr;
    }

}

在Activity類中同樣有一個成員變量mInstrumentation,在調用attach後把ActivityThread的mInstrumentation賦值給了Activity的mInstrumentation,後續每一個Activity在啓動時經過attach()方法,都會把該mInstrumentation傳入Activity賦給Activity.mInstrumentation, 也就是同一個應用進程內,所有Activity共用同一個Instrumentation。

三、UiAutomator2.0基於Instrumentation優缺點

    在android應用UI層的自動化中使用Instrumentation調用Android接口還是非常少見的,使用Instrumentation比較常見的是在Android單元測試。但這並不是說基於Instrumentation就沒有任何好處,只要能得到Instrumentation實例,它就可以遊刃有餘的擁抱應用本身,與Android應用實現無縫接觸,就可以使用Android服務及接口,比如得到Context,控制Activity的生命週期、按鍵操作等等。在這麼多好處的情況下,它的缺點很現實,就是不接受跨進程,前面說Instrumentation的時候也應該明白,在整個進程中從ActivityThread實例化Instrumentation後,無論是在Activity還是其他任何地方,都是唯一的Instrumentation。而如果是其他應用進程中,它又有另一個歸屬於該進程的Instrumentation實例。而UiAutomator2.0除了基於Instrumentation運行外,對於元素的查找與操作,依然支持Accessibility服務,這樣就可以完美的彌補Instrumentation不支持跨進程操作的缺陷了。

    另外要提的一點就是使用Instrumentation執行,會把測試代碼APK與被測應用APK都推送到設備中運行,這時候測試代碼的APK與被測應用APK處於同一進程中,這是基於Instrumentation運行測試的特點,所以它才能完成對被測應用的操作。如果被測應用並不是與UiAutomator2.0測試代碼APK不在同一進程,也完全可以使用,因爲它支持Accessibility服務對整個android系統事件的監控處理。

四、解決疑惑

在《Android基礎知識學習-Instrumentation啓動源碼簡析》的最後得出的結論

用adb shell am instrument命令執行測試,mInstrumentation這個實例指向的並不是默認的Instrumentation而是命令中指定的AndroidJUnitRunner,所以後面調用的是AndroidJUnitRunner的onCreate方法開始執行測試

爲了證明這個是對的,上代碼

1.被測應用

在Android Studio中新建項目,這個項目需要創建一個Activity,如MainActivity,代碼如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Field mInstrumentation;
        Object value = null;
        try {
            Class<?> activityThread = Class.forName("android.app.ActivityThread");
            Method sCurrentActivityThread = activityThread.getMethod("currentActivityThread");
            //獲取ActivityThread 對象
            Object activityThreadObject = sCurrentActivityThread.invoke(activityThread, new Object[0]);

            mInstrumentation = activityThread.getDeclaredField("mInstrumentation");
            mInstrumentation.setAccessible(true);
            value = mInstrumentation.get(activityThreadObject);
        }catch (Exception e){
            e.printStackTrace();
        }
        Log.d("debug", "Instrumentation: "+value.getClass().getName());
    }
}

通過反射得到ActivityThread對象,然後得到它的成員變量mInstrumentation,最終log打印出mInstrumentation對象的類名。這個思路應該是很清晰的,因爲進程中唯一的Instrumentation就是在ActivityThread中初始化的,所以反射得到ActivityThread對象。

2.測試應用

在項目androidTest目錄下會默認創建一個測試類ExampleInstrumentedTest

@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    private Context ct;
    @Before
    public void init(){
        ct = InstrumentationRegistry.getContext();

    }

    @Test
    public void useAppContext() {
        Intent testIntent = ct.getPackageManager().getLaunchIntentForPackage("ymxh.main");
        ct.startActivity(testIntent);
}

通過InstrumentationRegistry得到測試應用的Context,然後啓動App默認的Activity。

3.驗證

(1)手動運行

把應用打包成APK安裝在手機設備中,使用adb與手機設備連接上,然後手動點擊運行,在Android Studio的Logcat日誌輸出窗口中會打印

debug: Instrumentation: android.app.Instrumentation

(2)通過adb shell am instrument命令運行

這裏就是運行上面的ExampleInstrumentedTest類,日誌打印

debug: Instrumentation: android.support.test.runner.AndroidJUnitRunner

最終驗證可以看到,按正常流程使用app啓動應用,系統初始化的是默認的Instrumentation;而通過命令執行啓動應用,初始化的是指定的運行器。

很多文中說到UiAutomator1.0與UiAutomator2.0的區別都提到說UiAutomator2.0是android應用,需要打包成APK在設備運行;個人覺得這樣的說法都是在說它基於Instrumentation的特點,而並不是說UiAutomator2.0本身。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章