美文网首页Android技术经验
【Android】如何在不植入自己Sdk的前提下统计第三方App

【Android】如何在不植入自己Sdk的前提下统计第三方App

作者: 李翾 | 来源:发表于2017-06-09 17:55 被阅读330次

    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>

    相关文章

      网友评论

        本文标题:【Android】如何在不植入自己Sdk的前提下统计第三方App

        本文链接:https://www.haomeiwen.com/subject/imyfqxtx.html