Android平台上,一个App的启动时间可以说是一个重要的性能指标。如何获取一个App的启动时间呢,接下来咱们详细探讨一下。

  在查阅Android的文档之后发现,Android的shell命令里面是有这个功能的,打开adb,输入以下命令

        am是shell中集成的一个命令,ActivityManager的简写。一共需要提供两个参数-W,-n,其中-W是指启动完成之后,返回启动耗时,是最关键的一个参数。-n后面跟的是需要启动的App的包名和launchActivity。点击确定之后,会发现App被成功启动,且adb中会输入以下结果

        其中ThisTime即是本次App启动所花费的时间。

        到了这里我们基本完成了一半的工作,但是每次都需要在adb中才能查看启动时间无疑是很麻烦的,那么如何在手机上通过一个App控制其他App的启动,并获取启动时间呢,咱们继续看。

        首先通过shell看一下am命令中包含什么,在System/bin目录下面找到“am”命令,并打印出之后是如下的结果

       可以看出他跟普通的shell命令不一样,他是通过调用一个/framework/目录下的一个am.jar完成的工作,并最终执行com.android.commands.am包下面的Am.java。那我们接下来要做的工作就是下载Android源码,并找到这个Am.java。

        Am.java部分代码如下:

public class Am extends BaseCommand {    private IActivityManager mAm;    ...        public static void main(String[] args) {        (new Am()).run(args);    }    ...        @Override    public void onRun() throws Exception {        if (op.equals("start")) {            runStart();        }         ...    }    private void runStart() throws Exception {    ...    IActivityManager.WaitResult result = null;        int res;    if (mWaitOption) {            result = mAm.startActivityAndWait(null, null, intent, mimeType,                        null, null, 0, mStartFlags, mProfileFile, fd, null, mUserId);            res = result.result;        } else {            res = mAm.startActivityAsUser(null, null, intent, mimeType,                    null, null, 0, mStartFlags, mProfileFile, fd, null, mUserId);        }        ...    }}

        看到代码之后突然就有了一种豁然开朗的感觉,没错,就是main函数!他就是整个am命令的入口,并调用onRun方法。如果我们输入的参数是“start"的话,会调用runStart()方法。在runStart()方法之前的预处理中,根据我们输入的-W参数,将mWaitOption赋值为true。然后最终就找到了整个命令的精髓所在--startActivityAndWait()!

        到这一步之后,我们需要做的工作就是如何在自己的App中调用这个startActivityAndWait方法。

        不过很明显这个API是不对开发者开放的,Android系统中有很多隐藏API,Google之所以要将一些API隐藏(指加上@hide标记的public类、方法或常量)是因为Android系统本身还在不断的进化发展中。从1.01.1到现在4.4,这些隐藏的API本身可能是不稳定的(方法的参数数量,类型都会变化),所以使用隐藏API,意味着程序更差的兼容性。

        但这并不意味着我们就不能使用它,这些隐藏的API在系统中都是真实存在的,只是不对外开放而已。我们可以通过三种方法调用它,分别是:JAVA反射机制、API欺骗、重新编译Android源码。

        反射是Java的语言特性之一,在此不进行赘述,需要介绍一下API欺骗:烧制到手机中的android.jar包含了Android所需的各种类与方法;而供开发者使用的android.jar只是其中的一部分。API欺骗是指在应用中去模拟未公开的类和方法让应用编译通过并生成APK,然而在应用实际运行中调用的却仍是烧制到手机中真实的android.jar。

        通过查看源码我们可以看到需要模拟的是ActivityManagerNative、IActivityManager和他的startActivityAndWait方法。参照源码,实现以下代码:

package android.app;public abstract class ActivityManagerNative {public static IActivityManager getDefault() {return null;}}
package android.app;import android.content.ComponentName;import android.os.Parcel;import android.os.Parcelable;public abstract interface IActivityManager {public WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,            Intent intent, String resolvedType, IBinder resultTo, String resultWho,            int requestCode, int flags, String profileFile,            ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException;public static class WaitResult implements Parcelable {public int result;public boolean timeout;public ComponentName who;public long thisTime;public long totalTime;};}
        分别查阅4.0、4.4的源码之后发现startActivityAndWait的参数个数和顺序居然不一样,那通过API欺骗的方式适配所有的系统版本看来是不现实了。但是我们可以结合Java的反射和API欺骗来完成这个工作。所以改写后的代码如下:
package android.app;public abstract class ActivityManagerNative {public static IActivityManager getDefault() {return null;}}
package android.app;import android.content.ComponentName;import android.os.Parcel;import android.os.Parcelable;public abstract interface IActivityManager {public static class WaitResult implements Parcelable {public int result;public boolean timeout;public ComponentName who;public long thisTime;public long totalTime;};}
        然 在使用时,首先通过API欺骗获取到一个IActivityManager的实例,然后通过Java反射找到startActivityAndWait方法,根据不同的系统版本,传递不同的参数,最终完成工作,代码如下:
private void getStartActivityMethod() {activityManager = ActivityManagerNative.getDefault();Method[] methods = activityManager.getClass().getDeclaredMethods();for (int i = 0; i < methods.length; i++) {String methodName = methods[i].getName();if (methodName.contains("startActivityAndWait")) {startActivityMethod = methods[i];startActivityMethod.setAccessible(true);break;}}}// 4.4private long startActivityWithFieldsForApi19(Intent intent) {Object[] objects = new Object[] { null, null, intent, null, null, null, 0, 0, null, null, null, 0 };return startActivityForResult(objects);}private long startActivityForResult(Object[] objects) {try {Object object = startActivityMethod.invoke(activityManager, objects);WaitResult waitResult = (WaitResult) object;return waitResult.thisTime;} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}return -1;}
最后附上Android源码的下载地址: http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/

个人整理的不同系统版本startActivityAndWait()函数的详情:http://download.csdn.net/detail/yutou58nian/7049953

        

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. android上一些方法的区别和用法的注意事项
  5. android实现字体闪烁动画的方法
  6. Android(安卓)Wifi模块分析(三)
  7. Android中dispatchDraw分析
  8. Android四大基本组件介绍与生命周期
  9. Android(安卓)MediaPlayer 常用方法介绍

随机推荐

  1. android sqlite 介绍
  2. Android模拟器
  3. Android如何使布局中图(ImageButton)和文字
  4. Android实现2D翻转动画
  5. Android开发入门
  6. Android(安卓)WebView 开发详解(二)
  7. 【博客园客户端】博客园Android客户端更
  8. Android UI主线程与子线程
  9. Android屏幕适配攻略
  10. 如果你现在学Android---学习使用Kotlin进