【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>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容