Android如何获取APP启动时间



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部分代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class Am extends BaseCommand {  
  2.   
  3.     private IActivityManager mAm;  
  4.     ...  
  5.       
  6.     public static void main(String[] args) {  
  7.         (new Am()).run(args);  
  8.     }  
  9.   
  10.     ...  
  11.       
  12.     @Override  
  13.     public void onRun() throws Exception {  
  14.         if (op.equals("start")) {  
  15.             runStart();  
  16.         }   
  17.         ...  
  18.     }  
  19.   
  20.     private void runStart() throws Exception {  
  21.         ...  
  22.         IActivityManager.WaitResult result = null;  
  23.         int res;  
  24.         if (mWaitOption) {  
  25.             result = mAm.startActivityAndWait(nullnull, intent, mimeType,  
  26.                         nullnull0, mStartFlags, mProfileFile, fd, null, mUserId);  
  27.             res = result.result;  
  28.         } else {  
  29.             res = mAm.startActivityAsUser(nullnull, intent, mimeType,  
  30.                     nullnull0, mStartFlags, mProfileFile, fd, null, mUserId);  
  31.         }  
  32.         ...  
  33.     }  
  34. }  

        看到代码之后突然就有了一种豁然开朗的感觉,没错,就是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方法。参照源码,实现以下代码:

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

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

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部分代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class Am extends BaseCommand {  
  2.   
  3.     private IActivityManager mAm;  
  4.     ...  
  5.       
  6.     public static void main(String[] args) {  
  7.         (new Am()).run(args);  
  8.     }  
  9.   
  10.     ...  
  11.       
  12.     @Override  
  13.     public void onRun() throws Exception {  
  14.         if (op.equals("start")) {  
  15.             runStart();  
  16.         }   
  17.         ...  
  18.     }  
  19.   
  20.     private void runStart() throws Exception {  
  21.         ...  
  22.         IActivityManager.WaitResult result = null;  
  23.         int res;  
  24.         if (mWaitOption) {  
  25.             result = mAm.startActivityAndWait(nullnull, intent, mimeType,  
  26.                         nullnull0, mStartFlags, mProfileFile, fd, null, mUserId);  
  27.             res = result.result;  
  28.         } else {  
  29.             res = mAm.startActivityAsUser(nullnull, intent, mimeType,  
  30.                     nullnull0, mStartFlags, mProfileFile, fd, null, mUserId);  
  31.         }  
  32.         ...  
  33.     }  
  34. }  

        看到代码之后突然就有了一种豁然开朗的感觉,没错,就是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方法。参照源码,实现以下代码:

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

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


转载地址:http://m.blog.csdn.net/blog/yutou58nian/21176139

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值