Android源码学习之三-Activity是如何进行自动化测试的

时间:2021-09-27 04:50:10

SDK中为我们提供了非常好的对Activity进行测试的方式,那么Activity的内部机制是如何支持的呢?比如如何在Activity的生命周期变化中加入测试代码?如何监控到Activity的各种变化?如何获得Activity启动的性能参数?如何自动向Activity发送按键消息以进行测试?带着这些问题,我们要再次深入Activity源码来一探究竟。

首先想到的就是,如果要想监控到Activity生命周期的变化,必然会在CreateStart等处有代码来进行处理,所以我们把重点放在Activity那近4000行代码中与此相关的函数部分。

遗憾的是在onCreate()onStart()等处没有发现任何有效的线索,不过不要气馁,继续向下找。

startActivityForResult()函数中,mInstrumentation.execStartActivity()一句让我们看到至少有一个mInstrumentation的对象受委托在启动Activity,这个委托是有可能加入监控动作的。

    public void startActivityForResult(Intent intent, int requestCode) {

        if (mParent == null) {

            Instrumentation.ActivityResult ar =

                mInstrumentation.execStartActivity(

                    this, mMainThread.getApplicationThread(), mToken, this,

                    intent, requestCode);

            if (ar != null) {

                mMainThread.sendActivityResult(

                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),

                    ar.getResultData());

            }

            if (requestCode >= 0) {

                mStartedActivity = true;

            }

        } else {

            mParent.startActivityFromChild(this, intent, requestCode);

        }

    }

继续向下,在以perform开头的几个函数中,我们再次发现了对mInstrumentation对象的调用。例如performStart()函数。

    final void performStart() {

        mCalled = false;

        mInstrumentation.callActivityOnStart(this);

        if (!mCalled) {

            throw new SuperNotCalledException(

                "Activity " + mComponent.toShortString() +

                " did not call through to super.onStart()");

        }

    }

到此为止,我们必须得对Instrumentation类进行深入了解了,方式有二种,一是通过SDK了解该类的功能和接口,二是直接看源码。如果二种方式都用也没有问题,先通过SDK,我们知道了该类是一个基类,会在我们自己的应用程序运行之前被系统实例化,以允许我们监控所有与系统的交互(太伟大了),实现的方式是通过在AndroidManifest.xml文件中加入 <instrumentation> 标签。看来这个一定是我们想要找到的答案了,赶紧打开源码来学习一下吧。

整体上看,该类的接口主要有以下几类:对Activity监控对象的处理接口,Activity状态改变时的回调通知接口,生成ActivityApplication的接口,向Activity发送事件通知的接口,性能处理的接口等。

下面我们就找几个典型的函数来看一下InstrumentationActivity的交互机制。

先来看callActivityOnCreate()函数。

    public void callActivityOnCreate(Activity activity, Bundle icicle) {

        if (mWaitingActivities != null) {

            synchronized (mSync) {

                final int N = mWaitingActivities.size();

                for (int i=0; i<N; i++) {

                    final ActivityWaiter aw = mWaitingActivities.get(i);

                    final Intent intent = aw.intent;

                    if (intent.filterEquals(activity.getIntent())) {

                        aw.activity = activity;

                        mMessageQueue.addIdleHandler(new ActivityGoing(aw));

                    }

                }

            }

        }

       

        activity.onCreate(icicle);

       

        if (mActivityMonitors != null) {

            synchronized (mSync) {

                final int N = mActivityMonitors.size();

                for (int i=0; i<N; i++) {

                    final ActivityMonitor am = mActivityMonitors.get(i);

                    am.match(activity, activity, activity.getIntent());

                }

            }

        }

    }

注意activity.onCreate(icicle);这句的含义,调用了我们自己的Activity重写的onCreate()函数。

在来看我们前文提到的execStartActivity()函数。

    public ActivityResult execStartActivity(

        Context who, IBinder contextThread, IBinder token, Activity target,

        Intent intent, int requestCode) {

                   ……// 处理ActivityMonitor

        try {

            int result = ActivityManagerNative.getDefault()

                .startActivity(whoThread, intent,

                        intent.resolveTypeIfNeeded(who.getContentResolver()),

                        null, 0, token, target != null ? target.mEmbeddedID : null,

                        requestCode, false, false);

            checkStartActivityResult(result, intent);

        } catch (RemoteException e) {

        }

        return null;

    }

在这里,处理完ActivityMonitor后,真正启动一个Activity的是ActivityManagerNative.getDefault().startActivity()语句,这也符合我们的预期,真正的Activity的操作应该是由ActivityManager来完成的,当然对于ActivityManager来说,内部还有很复杂的代码和类关系,以后有时间我们再来一一拆解吧。

还有一个很重要的函数startActivitySync(),这是一个同步方式启动Activity的功能,在正常的startActivity()之后,一直进入等待状态,直到Activity运行起来该函数才返回,这样,当我们想知道一个Activity的启动性能的时候,这个是非常重要的实现。

    public Activity startActivitySync(Intent intent) {

                            ……

            getTargetContext().startActivity(intent);

 

            do {

                try {

                    mSync.wait();

                } catch (InterruptedException e) {

                }

            } while (mWaitingActivities.contains(aw));

                            ……

}

 

余下像性能快照、事件发送等功能就留给大家自己研究了。

最后要说的是对Instrumentation的使用是要实现自己的子类的,并且为我们预留了onCreate()onStart()等接口,在Android的测试包里也有子类的示例,可以参考。另外别忘了要在AndroidManifest.xml文件中加入 <instrumentation> 标签才行。

——欢迎转载,转载请注明出处,谢谢——