【Android】如何在不植入自己Sdk的前提下統計第三方App啟動次數,首次安裝時間

1.針對5.0以上的安卓系統,有系統API:可以使用android.app.usage.UsageStatsManager,閑話不多說,直接po代碼如下:

   mUsageStatsManager=       (UsageStatsManager).getSystemService(Context.USAGE_STATS_SERVICE);

     Calendar cal = Calendar.getInstance();

     cal.add(Calendar.YEAR, -1);

    List queryUsageStats = getUsageStatistics(1);

    PackageManager pm = getApplicationContext().getPackageManager();

  for(inti =0; i < queryUsageStats.size(); i++) {

        UsageStats usageStats = queryUsageStats.get(i);

         Class c = usageStats.getClass();

  try{

//mLaunchCount 我理解的它就是是app啟動次數,這是被@hide修飾的成員變量。只能用反射得到它的            值。但是有個現象是,啟動次不

  //準,從桌面點進去記數2,從后臺進入記數1,時間間隔為一天,總的統計次數也不準確



          Field field = c.getDeclaredField("mLaunchCount");

           field.setAccessible(true);

           Field field1 = c.getDeclaredField("mBeginTimeStamp");

           field1.setAccessible(true);

           Field field2 = c.getDeclaredField("mEndTimeStamp");

           field2.setAccessible(true);

            ApplicationInfo applicationInfo = pm.getApplicationInfo(usageStats.getPackageName(),                  PackageManager.GET_META_DATA);

           String appName = pm.getApplicationLabel(applicationInfo).toString();

           PackageInfo packageInfo = pm.getPackageInfo(usageStats.getPackageName(),0);

//應用首次裝時間

            longfirstInstallTime = packageInfo.firstInstallTime;

//格式化成年月日

            String firstInstallTime1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", firstInstallTime);

//應用最后一次更新時間

           long lastUpdateTime = packageInfo.lastUpdateTime;

           String lastUpdateTime1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", lastUpdateTime);

            Log.i("應用:"+usageStats.getPackageName(),"first install time : "+ firstInstallTime1 +" last update time :"+ lastUpdateTime1);

//這部分的時間,我沒有搞太明白,請自行驗證

    long firstTimeStamp = usageStats.getFirstTimeStamp();

  String firstTimeStamp1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", firstTimeStamp);

    long LastTimeStamp = usageStats.getLastTimeStamp();

  String LastTimeStamp1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", LastTimeStamp);

    long LastTimeUsed = usageStats.getLastTimeUsed();

  String LastTimeUsed1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", LastTimeUsed);

  long mBeginTimeStamp = field1.getLong(usageStats);

  String mBeginTimeStamp1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", mBeginTimeStamp);

    long mEndTimeStamp = field2.getLong(usageStats);

  String mEndTimeStamp1 = transferLongToDate("yyyy-MM-dd HH:mm:ss", mEndTimeStamp);

//這部分的時間,我沒有搞太明白,請自行驗證

            Log.i("使用列表...field...","應用名:"+ appName +"\n包名:"+ usageStats.getPackageName() +"\nfirstTimeStamp:"+               firstTimeStamp1

  +"\nLastTimeStamp:"+ LastTimeStamp1 +"\nLastTimeUsed:"+ LastTimeUsed1

  +",\n啟動次數:"+ field.getInt(usageStats)

    +",\nmBeginTimeStamp:"+mBeginTimeStamp1+",\nmEndTimeStamp:"+mEndTimeStamp1);

    }catch(Exception e) {

           e.printStackTrace();

  }

  }


  public List<UsageStats> getUsageStatistics(intintervalType) {
  // Get the app statistics since one year ago from the current time.

  Calendar cal = Calendar.getInstance();

  cal.add(Calendar.YEAR, -1);

    List queryUsageStats =mUsageStatsManager

  .queryUsageStats(UsageStatsManager.INTERVAL_DAILY, cal.getTimeInMillis(),

    System.currentTimeMillis());

    if(queryUsageStats.size() ==0) {

    Log.i("使用列表...","The user may not allow the access to apps usage. ");

    Toast.makeText(MainActivity.this,

    getString(R.string.action_settings),

      Toast.LENGTH_LONG).show();

  //            mOpenUsageSettingButton.setVisibility(View.VISIBLE);

  //            mOpenUsageSettingButton.setOnClickListener(new View.OnClickListener() {

    //                @Override                public void onClick(View v) {

    //                    startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

    //                }

    //            });

    startActivity(newIntent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

  }

  returnqueryUsageStats;

  }
  1. 5.0以下的系統就比較復雜了,需要系統權限,結論也需要驗證,方法如下:
  /**
 * Use reflect to get Package Usage Statistics data.<br>
 */
public void getPkgUsageStats() {


    Log.d(TAG, "[getPkgUsageStats]");
    try {
        Class<?> cServiceManager = Class
                .forName("android.os.ServiceManager");
        Method mGetService = cServiceManager.getMethod("getService",
                java.lang.String.class);
        Object oRemoteService = mGetService.invoke(null, "usagestats");

        // IUsageStats oIUsageStats =
        // IUsageStats.Stub.asInterface(oRemoteService)
        Class<?> cStub = Class
                .forName("com.android.internal.app.IUsageStats$Stub");
        Method mUsageStatsService = cStub.getMethod("asInterface",
                android.os.IBinder.class);
        Object oIUsageStats = mUsageStatsService.invoke(null,
                oRemoteService);

        // PkgUsageStats[] oPkgUsageStatsArray =
        // mUsageStatsService.getAllPkgUsageStats();
        Class<?> cIUsageStatus = Class
                .forName("com.android.internal.app.IUsageStats");
        Method mGetAllPkgUsageStats = cIUsageStatus.getMethod(
                "getAllPkgUsageStats", (Class[]) null);
        Object[] oPkgUsageStatsArray = (Object[]) mGetAllPkgUsageStats
                .invoke(oIUsageStats, (Object[]) null);
        Log.d(TAG, "[getPkgUsageStats] oPkgUsageStatsArray = " + oPkgUsageStatsArray);

        Class<?> cPkgUsageStats = Class
                .forName("com.android.internal.os.PkgUsageStats");

        StringBuffer sb = new StringBuffer();
        sb.append("nerver used : ");
        for (Object pkgUsageStats : oPkgUsageStatsArray) {
            // get pkgUsageStats.packageName, pkgUsageStats.launchCount,
            // pkgUsageStats.usageTime
            String packageName = (String) cPkgUsageStats.getDeclaredField(
                    "packageName").get(pkgUsageStats);
            int launchCount = cPkgUsageStats
                    .getDeclaredField("launchCount").getInt(pkgUsageStats);
            long usageTime = cPkgUsageStats.getDeclaredField("usageTime")
                    .getLong(pkgUsageStats);
            if (launchCount > 0)
                Log.d(TAG, "[getPkgUsageStats] " + packageName + "  count: "
                        + launchCount + "  time:  " + usageTime);
            else {
                sb.append(packageName + "; ");
            }
        }
        Log.d(TAG, "[getPkgUsageStats] " + sb.toString());
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
}

這是獲取信息的兩種實現方式,另外,要想讓程序能夠正常運行并成功獲取到數據,我們還需要做如下的配置:
1、在AndroidManifest.xml中增加android:sharedUserId="android.uid.system"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="android.uid.system" >
還有權限的聲明
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />

2、對apk進行系統簽名,在源碼中取platform.pk8、platform.x509.pem、signapk.jar文件并通過如下命令實現apk的簽名
java -jar signapk.jar platform.x509.pem platform.pk8 unsigned.apk signed.apk
unsigned.apk為簽名之前的apk,signed.apk為通過命令簽名成功的apk

<big>作個總結:
結論是半憂半喜的,關于5.0以上系統統計啟動次數,系統提供API不準,如果不在第三方應用植入自己的sdk,統計第三方應用的啟動次數不靠譜,但是要收集安裝時間是可以的。

</big>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容