Android Instrumentation源码分析(附Activity启动流程)

转载请注明出处:http://blog.csdn.net/ahence/article/details/54959235

Instrumentation概念

官方说明

Instrumentation类位于android.app包中,继承自java.lang.Object,一些测试用类如InstrumentationTestRunner或MultiDexTestRunner直接或间接继承自该类。官方对于该类的解释如下:

Base class for implementing application instrumentation code. When running with instrumentation turned on, this class will be instantiated for you before any of the application code, allowing you to monitor all of the interaction the system has with the application. An Instrumentation implementation is described to the system through an AndroidManifest.xml’s instrumentation tag.

大意是当instrumentation开启的话,它会在应用程序的任何组件创建之前初始化,可以用来监控系统与应用的所有交互。系统可以根据AndroidManifest.xml中的 instrumentation 标签来实现instrumentation。

在AndroidManifest.xml中的声明方式及属性解释

语法:

<instrumentation android:functionalTest=["true" | "false"]
                 android:handleProfiling=["true" | "false"]
                 android:icon="drawable resource"
                 android:label="string resource"
                 android:name="string"
                 android:targetPackage="string" />

父元素:

manifest

描述:

声明一个Instrumentation类,可以用来监控应用与系统之间的交互。Instrumentation类的对象会在应用的所有组件创建之前初始化。

属性介绍:

android:functionalTest

指定Instrumentation类是否作为一个功能性的测试来运行,如果为true,则是;如果为false,则不是,默认值为false。

android:handleProfiling

指定Instrumentation对象是否开启和关闭分析功能。如果设置为true,那么由Instrumentation对象来决定何时启动、停止分析;如果设置为false,则分析功能会在Instrumentation对象整个运行期间启用。如果设置为true,可以使Instrumentation对象针对一组特定的操作来进行分析,默认值为false。

android:icon

Instrumentation类呈现给用户的图标,这个属性必须设置为一个Drawable资源的引用。

android:label

用户可读的Instrumentation类的标签,这个标签可以被设置为原生字符串或者字符串资源的引用。

android:name

Instrumentation子类的名称,它应当是完整的类名(如,“com.example.project.StringInstrumentation”)。然而,还有一种简写方式,如果这个名称的第一个字符是英文句号,那么它将追加到manifest元素指定的包名的后面。

它没有默认值,必须要设置。

android:targetPackage

Instrumentation对象将要运行的应用。这个应用由在它的manifest文件中由manifest元素指定的包名来确定。

Instrumentation源码分析

构造函数
public Instrumentation() {
}
Instrumentation生命周期相关的方法

首先是onCreate方法:

/**
 * Called when the instrumentation is starting, before any application code
 * has been loaded.  Usually this will be implemented to simply call
 * {@link #start} to begin the instrumentation thread, which will then
 * continue execution in {@link #onStart}.
 * 
 * <p>If you do not need your own thread -- that is you are writing your
 * instrumentation to be completely asynchronous (returning to the event
 * loop so that the application can run), you can simply begin your
 * instrumentation here, for example call {@link Context#startActivity} to
 * begin the appropriate first activity of the application. 
 *  
 * @param arguments Any additional arguments that were supplied when the 
 *                  instrumentation was started.
 */
public void onCreate(Bundle arguments) {
}

当启动一个instrumentation时会调用上述onCreate方法,它会在所有应用程序代码加载完成之前启动。在Instrumentation源码中,它是一个空方法,因此会在其实现类中重写,如在InstrumentationTestRunner中就重写了onCreate,并在最后调用了start()方法。

start方法会启动一个新的线程用来执行instrumentation,代码如下:

/**
 * Create and start a new thread in which to run instrumentation.  This new
 * thread will call to {@link #onStart} where you can implement the
 * instrumentation.
 */
public void start() {
    if (mRunner != null) {
        throw new RuntimeException("Instrumentation already started");
    }
    mRunner = new InstrumentationThread("Instr: " + getClass().getName());
    mRunner.start();
}

其中mRunner是InstrumentationThread的一个实例,表示Instrumentation所在的线程,该线程启动后会执行onStart()方法:

private final class InstrumentationThread extends Thread {
   
    public InstrumentationThread(String name) {
        super(name);
    }
    public void run() {
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
        } catch (RuntimeException e) {
            Log.w(TAG, "Exception setting priority of instrumentation thread "
                    + Process.myTid(), e);
        }
        if (mAutomaticPerformanceSnapshots) {
            startPerformanceSnapshot();
        }
        onStart();
    }
}

接下来看onStart方法:

/**
 * Method where the instrumentation thread enters execution.  This allows
 * you to run your instrumentation code in a separate thread than the
 * application, so that it can perform blocking operation such as
 * {@link #sendKeySync} or {@link #startActivitySync}.
 * 
 * <p>You will typically want to call finish() when this function is done,
 * to end your instrumentation.
 */
public void onStart() {
}

在onStart()里面可以具体实现instrumentation的逻辑,但在Instrumentation中它也是个空方法,需要重写该方法,从而可以测试一些阻塞性的操作,当功能完成时可以调用finish()方法来结束instrumentation,如下:

/**
 * Terminate instrumentation of the application.  This will cause the
 * application process to exit, removing this instrumentation from the next
 * time the application is started. 
 *  
 * @param resultCode Overall success/failure of instrumentation. 
 * @param results Any results to send back to the code that started the 
 *                instrumentation.
 */
public void finish(int resultCode, Bundle results) {
    if (mAutomaticPerformanceSnapshots) {
        endPerformanceSnapshot();
    }
    if (mPerfMetrics != null) {
        if (results == null) {
            results = new Bundle();
        }
        results.putAll(mPerfMetrics);
    }
    if ((mUiAutomation != null) && !mUiAutomation.isDestroyed()) {
        mUiAutomation.disconnect();
        mUiAutomation = null;
    }
    mThread.finishInstrumentation(resultCode, results);
}

finish方法会终止应用程序的instrumentation,同时应用进程退出,下次该应用再次启动时会移除该instrumentation。

最后是onDestroy方法:

/**
 * Called when the instrumented application is stopping, after all of the
 * normal application cleanup has occurred.
 */
public void onDestroy() {
}

此方法当应用程序停止检测,且所有应用被清理之后才会执行。

获取Context的两个方法

第一个是获取instrumentation所在包的上下文对象:

/**
 * Return the Context of this instrumentation's package.  Note that this is
 * often different than the Context of the application being
 * instrumentated, since the instrumentation code often lives is a
 * different package than that of the application it is running against.
 * See {@link #getTargetContext} to retrieve a Context for the target
 * application.
 * 
 * @return The instrumentation's package context.
 * 
 * @see #getTargetContext
 */
public Context getContext() {
    return mInstrContext;
}

第二个是获取被检测的应用的上下文对象:

/**
 * Return a Context for the target application being instrumented.  Note
 * that this is often different than the Context of the instrumentation
 * code, since the instrumentation code often lives is a different package
 * than that of the application it is running against. See
 * {@link #getContext} to retrieve a Context for the instrumentation code.
 * 
 * @return A Context in the target application.
 * 
 * @see #getContext
 */
public Context getTargetContext() {
    return mAppContext;
}

为什么会有上面两个不同的Context呢?是因为instrumentation的代码经常运行在另一个包(或者叫进程)中,而被检测的应用是另一个进程。

控制性能分析的几个方法
/**
 * Check whether this instrumentation was started with profiling enabled.
 * 
 * @return Returns true if profiling was enabled when starting, else false.
 */
public boolean isProfiling() {
    return mThread.isProfiling();
}

/**
 * This method will start profiling if isProfiling() returns true. You should
 * only call this method if you set the handleProfiling attribute in the 
 * manifest file for this Instrumentation to true.  
 */
public void startProfiling() {
    if (mThread.isProfiling()) {
        File file = new File(mThread.getProfileFilePath());
        file.getParentFile().mkdirs();
        Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
    }
}

/**
 * Stops profiling if isProfiling() returns true.
 */
public void stopProfiling() {
    if (mThread.isProfiling()) {
        Debug.stopMethodTracing();
    }
}
public void setAutomaticPerformanceSnapshots() {
    mAutomaticPerformanceSnapshots = true;
    mPerformanceCollector = new PerformanceCollector();
}

public void startPerformanceSnapshot() {
    if (!isProfiling()) {
        mPerformanceCollector.beginSnapshot(null);
    }
}

public void endPerformanceSnapshot() {
    if (!isProfiling()) {
        mPerfMetrics = mPerformanceCollector.endSnapshot();
    }
}

上面几个方法比较简单,通过注释即可理解其用途。

两个比较重要的内部类
Instrumentation.ActivityResult

该类大家应该很熟悉,我们经常使用startActivityForResult,这个类就是用来封装从第二个Activity返回的结果的,其源码如下:

/**
 * Description of a Activity execution result to return to the original
 * activity.
 */
public static final class ActivityResult {
   
    /**
     * Create a new activity result.  See {@link Activity#setResult} for 
     * more information. 
     *  
     * @param resultCode The result code to propagate back to the
     * originating activity, often RESULT_CANCELED or RESULT_OK
     * @param resultData The data to propagate back to the originating
     * activity.
     */
    public ActivityResult(int resultCode, Intent resultData) {
        mResultCode = resultCode;
        mResultData = resultData;
    }

    /**
     * Retrieve the result code contained in this result.
     */
    public int getResultCode() {
        return mResultCode;
    }

    /**
     * Retrieve the data contained in this result.
     */
    public Intent getResultData() {
        return mResultData;
    }

    private final int mResultCode;
    private final Intent mResultData;
}

该类非常简单,维护了一个整型的resultCode以及一个Intent类型的resultData,可以结合常用的onActivityResult回调来印证一下。

Instrumentation.ActivityMonitor

该类官方注释如下:

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值