Android 使用记录访问权限

o287r434i 8年前
   <p>什么是使用记录访问权限呢?这是在Android5.0(Api level 21)新添加的,通过该权限我们可以查看设备上其它应用使用情况的统计信息等。</p>    <p>如何使用该权限呢?</p>    <p>首先在manifest中添加:</p>    <pre>  <code class="language-java"><uses-permission          android:name="android.permission.PACKAGE_USAGE_STATS"          tools:ignore="ProtectedPermissions" /></code></pre>    <p>由于该权限默认只授予系统应用,所以添加了 ignore 属性。</p>    <p>然后通过如下代码进而手动打开权限:</p>    <pre>  <code class="language-java">Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);  startActivityForResult(intent);</code></pre>    <p>当然只要我们在manifest中进行了权限配置,也可以通过 <strong>设置->安全->有权查看使用情况的应用</strong> 来打开权限:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0f39b054fb413b82f4d21ce3a4ab5139.png"></p>    <p>到此我们的应用就拥有了该权限。那么有了这个权限到底能做什么呢?继续往下看......</p>    <p>前段时间和同事聊到了一个叫 <strong>我要当学霸</strong> 的app,里边有个学习监督的功能,就需要使用记录访问权限,当打开权限后,除了自己和桌面外,其它app都不能正常使用,点击其它app时会直接退到后台并弹出一个提示页面。不妨我们来模拟下这个功能。</p>    <p>在这之前我们首先看一个类 <strong>UsageStatsManager</strong> :</p>    <pre>  <code class="language-java">public final class UsageStatsManager {      public static final int INTERVAL_BEST = 4; //根据提供的开始、结束时间决定时间间隔      public static final int INTERVAL_DAILY = 0; //以天为时间间隔(最长7天)      public static final int INTERVAL_MONTHLY = 2; //以月为时间间隔(最长6个月)      public static final int INTERVAL_WEEKLY = 1; //以周为时间间隔(最长4个星期)      public static final int INTERVAL_YEARLY = 3; //以年为时间间隔(最长2年)        UsageStatsManager() {          throw new RuntimeException("Stub!");      }        public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {          throw new RuntimeException("Stub!");      }        public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime, long endTime) {          throw new RuntimeException("Stub!");      }        public UsageEvents queryEvents(long beginTime, long endTime) {          throw new RuntimeException("Stub!");      }        public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {          throw new RuntimeException("Stub!");      }        public boolean isAppInactive(String packageName) {          throw new RuntimeException("Stub!");      }  }</code></pre>    <p>可以看到该类提供了五种时间间隔类型,这里我们比较关注 queryUsageStats() 方法,通过该方法我们可以得到一段时间内 其它应用的使用情况。</p>    <p>我们实现思路是这样的,通过UsageStatsManager类获得2秒内手机app的使用数据,找到时间最近的一个,如果不是我们自己的app或桌面则模拟home键点击,同时弹出一个提示页面,具体的代码如下:</p>    <pre>  <code class="language-java">private void getTopApp() {          UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);//usagestats          long time = System.currentTimeMillis();          List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, time - 2000, time);            if (usageStatsList != null && !usageStatsList.isEmpty()) {              SortedMap<Long, UsageStats> usageStatsMap = new TreeMap<>();              for (UsageStats usageStats : usageStatsList) {                  usageStatsMap.put(usageStats.getLastTimeUsed(), usageStats);              }              if (!usageStatsMap.isEmpty()) {                  String topPackageName = usageStatsMap.get(usageStatsMap.lastKey()).getPackageName();                    if (getLauncherPackageName(mContext).equals(topPackageName) || "com.othershe.test".equals(topPackageName)) {                      return;                  }                    Log.e("TopPackage Name", topPackageName);                    //模拟home键点击                  Intent intent = new Intent(Intent.ACTION_MAIN);                  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                  intent.addCategory(Intent.CATEGORY_HOME);                  startActivity(intent);                    //启动提示页面                  Intent intent1 = new Intent(mContext, TipActivity.class);                  intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                  startActivity(intent1);              }          }      }</code></pre>    <p>因为时间周期是2秒,所以这里我们采用 <strong>INTERVAL_BEST</strong> 作为时间间隔。其中的 <strong>UsageStats</strong> 对象对应一个查询到的app数据,主要包含以下信息:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/306b40af4da18e097e2c4c139331bd9c.png"></p>    <p>getTopApp()是我们的核心方法,当然我们需要开启一个服务,然后在服务中每隔500毫秒执行一次上边的方法,这样就能起到不断检测的作用:</p>    <pre>  <code class="language-java">@Override      public int onStartCommand(Intent intent, int flags, int startId) {            mTimer = new Timer();          TimerTask task = new TimerTask() {              @Override              public void run() {                  getTopApp();              }          };            mTimer.schedule(task, 1000, 500);          return super.onStartCommand(intent, flags, startId);      }</code></pre>    <p>打开权限、启动服务,可以看到实际的运行效果如下,基本符合我们的预期。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/06944024df37a8206d8e99088797fbbc.gif"></p>    <p>类似的道理,我们也可以判断摸个app是否在前台运行。</p>    <p>上边我们使用了 <strong>INTERVAL_BEST</strong> 时间间隔类型,还可以使用其它4中,例如使用INTERVAL_YEARLY:</p>    <pre>  <code class="language-java">private void getHistoryApps() {          Calendar calendar = Calendar.getInstance();          long endTime = calendar.getTimeInMillis();          calendar.add(Calendar.YEAR, -1);          long startTime = calendar.getTimeInMillis();            UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);          List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, startTime, endTime);            if (usageStatsList != null && !usageStatsList.isEmpty()) {              HashSet<String> set = new HashSet<>();              for (UsageStats usageStats : usageStatsList) {                  set.add(usageStats.getPackageName());              }                if (!set.isEmpty()) {                  Log.e("size", set.size() + "");              }          }      }</code></pre>    <p>上边的代码我们最终获得了过去一年手机上使用过的app的包名集合(其中包括系统级别的):</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/32850a0ca0fb814a8339ab37ed31557e.png"></p>    <p>拿到这些包名可以做什么呢?</p>    <p>其实对于网赚类型的应用有这样一种业务场景,就是用户通过下载app来做任务进而赚取收益,但是如果当前设备通过其它网赚应用已经下载过某个app,然后卸载了,再通过你的网赚应用下载。如果你不知道用户之前安装过该app,就需要给用户结算相应的收益,但是你的上游渠道是不会给你结算的,因为这属于同一设备上的重复下载,这样对公司而言就是亏损的。</p>    <p>有了历史包名信息,我们就可以判断用户在一定的时间周期内是否安装过对应的app,进而采取相应的策略,这样可以在一定程度降低损失。当然有个前提,你要友好的引导用户开启改权限。</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/e11cdfaf15dc</p>    <p> </p>