Android如何获取APP启动时间

news/2024/7/7 15:38:26



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


http://www.niftyadmin.cn/n/3649415.html

相关文章

分享我的第一个Web作品——纯静态网站

以下是一年前学习Web基础开发时候的期末作品——计算机学习网。当时刚开始学习HTML和CSS。 网站采用HTMLCSSJavaScript的架构设计&#xff0c;当时大概用了12天左右&#xff0c;当然这12天还在上其他的课&#xff0c;这是利用课余时间做的。网站包括主页、内容页、登录和注册页…

javascript闭包_JavaScript的闭包和咖喱介绍

javascript闭包介绍 (Introduction) If you write code in JavaScript it’s quite likely you have come across the term closure, which is a useful yet often confusing concept. But just what is a closure? 如果您使用JavaScript编写代码&#xff0c;很可能会遇到术语…

GitHub上史上最全的Android开源项目分类汇总(一)

今天晚上配置好GitHub&#xff0c;顺便看看KJFrame,发现有很多的Git资源&#xff0c;下面有很多让人欣喜的效果&#xff0c;大家抓紧搬轮子~~ 浩 2014-11-26 今天在看博客的时候&#xff0c;无意中发现了Trinea在GitHub上的一个项目Android开源项目分类汇总&#xff0c;由于…

Windows安装OpenSSH服务-远程连接Linux

OpenSSH是 SSH (Secure SHell) 协议的免费开源实现。SSH协议族可以用来进行远程控制&#xff0c; 或在计算机之间传送文件。而实现此功能的传统方式&#xff0c;如telnet(终端仿真协议)、 rcp ftp、 rlogin、rsh都是极为不安全的&#xff0c;并且会使用明文传送密码。OpenSSH提…

Picasso的封装(二)

public class PicassoUtils {//默认加载图片public static void loadImaheView(Context mContext, String url, ImageView imageView) {Picasso.with(mContext).load(url).into(imageView);}//默认加载图片(指定大小)public static void loadImageViewSize(Context mContext, S…

如何创建您的第一个Visual Studio代码扩展

介绍 (Introduction) Visual Studio Code is a code editor from Microsoft available on Windows, Linux, and macOS. It offers extensions that you can install through the Visual Studio Code MarketPlace for additional features in your editor. When you can’t find…

网站添加免费SSL证书——HTTPS协议

在添加证书之前首先了解两个概念&#xff1a;SSL和HTTPS。 ▶ SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security&#xff0c;TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。 ▶ …

使用React Native Web构建适合移动设备的Web应用

介绍 (Introduction) Over the years, building web applications that are mobile friendly has become easier with the advent of media queries and the introduction of service workers. Using media queries, we could make web applications that different shapes whe…