通過api欺騙獲取安卓應用的啓動時間

原文地址:http://m.blog.csdn.net/blog/yutou58nian/21176139


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 plaincopy在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 plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package android.app;  
  2.   
  3. public abstract class ActivityManagerNative {  
  4.   
  5.     public static IActivityManager getDefault() {  
  6.         return null;  
  7.     }  
  8. }  
[java] view plaincopy在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 plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package android.app;  
  2.   
  3. public abstract class ActivityManagerNative {  
  4.   
  5.     public static IActivityManager getDefault() {  
  6.         return null;  
  7.     }  
  8. }  
[java] view plaincopy在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 plaincopy在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

發佈了68 篇原創文章 · 獲贊 14 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章