【后台任务】后台优化(15)

概要


后台进程可能是内存和电池密集型的。例如,隐式广播可能会启动许多后台进程,这些后台进程已经注册来监听它,即使这些进程可能没有太多工作。这会对设备性能和用户体验产生重大影响。

为了缓解这个问题,Android 7.0(API等级24)应用了以下限制:

  • 定位到Android 7.0(API级别24)及更高版本的应用程序CONNECTIVITY_ACTION如果在清单中声明其广播接收器,则不会收到 广播。应用程序仍然会收到CONNECTIVITY_ACTION,如果他们注册的广播节目BroadcastReceiver与Context.registerReceiver() 和这方面仍然有效。
  • 应用程序无法发送或接收ACTION_NEW_PICTURE或ACTION_NEW_VIDEO广播。此优化会影响所有应用程序,而不仅仅是针对Android 7.0(API级别24)的应用程序。
    如果您的应用程序使用这些意图中的任何一种,则应尽快删除它们的依赖关系,以便您可以正确定位运行Android 7.0的设备。Android框架提供了几种解决方案来减轻对这些隐式广播的需求。例如,JobScheduler当新的WorkManager提供强大的机制来满足特定条件(例如与未计量网络的连接)时的网络操作。您现在也可以使用JobScheduler 对内容提供者的更改作出反应。JobInfo 对象封装了JobScheduler 用于调度作业的参数。当作业的条件满足时,系统会在您的应用上执行此作业JobService。

在本文中,我们将学习如何使用其他方法,例如 JobScheduler,使您的应用适应这些新的限制。

接收网络活动广播的限制


面向Android 7.0(API级别24)的应用程序CONNECTIVITY_ACTION如果注册接收它们的清单中的广播,则不会启动依赖此广播的进程。如果应用程序想要监听网络更改或在设备连接到未计量的网络时执行批量网络活动,这可能会造成问题。解决此限制的几种解决方案已经存在于Android框架中,但是选择正确的解决方案取决于您希望应用程序完成的任务。

注意事项:一BroadcastReceiver,注册 Context.registerReceiver() 继续应用程序正在运行,而接收这些广播。

在未计量的连接上安排网络作业

使用JobInfo.Builder该类构建JobInfo对象时,应用该setRequiredNetworkType()方法并将其JobInfo.NETWORK_TYPE_UNMETERED作为工作参数传递。以下代码示例计划在设备连接到未收费网络并正在收费时运行的服务:

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
      (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo job = new JobInfo.Builder(
    MY_BACKGROUND_JOB,
    new ComponentName(context, MyJobService.class))
      .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
      .setRequiresCharging(true)
      .build();
  js.schedule(job);
}

当您的工作条件满足时,您的应用程序会收到一个回调以运行onStartJob()指定的方法JobService.class。要查看更多JobScheduler实现示例,请参阅JobScheduler示例应用程序。

WorkScheduler的一个新选择是WorkManager,它是一个API,它允许您安排需要保证完成的后台任务,而不管应用程序进程是否在附近。WorkManager根据设备API级别等因素选择合适的方式来运行工作(直接在应用程序进程中的线程上以及使用JobScheduler,FirebaseJobDispatcher或AlarmManager)。此外,WorkManager不需要播放服务,并提供多项高级功能,例如将任务链接在一起或检查任务的状态。要了解更多信息,请参阅WorkManager。

在应用程序运行时监控网络连接

应用程序运行仍然可以侦听CONNECTIVITY_CHANGE与注册BroadcastReceiver。但是,ConnectivityManager仅当满足指定的网络条件时,API才提供更强大的方法来请求回调。

NetworkRequest对象定义了网络回调的参数NetworkCapabilities。您可以NetworkRequest使用NetworkRequest.Builder该类创建对象。registerNetworkCallback() 然后将该NetworkRequest对象传递给系统。当网络条件满足时,应用程序会收到回调以执行onAvailable()其ConnectivityManager.NetworkCallback类中定义的 方法。

应用程序继续接收回调,直到应用程序退出或拨打电话 unregisterNetworkCallback()。

限制接收图像和视频广播


在Android 7.0(API等级24)中,应用程序无法发送或接收ACTION_NEW_PICTURE或ACTION_NEW_VIDEO广播。此限制有助于缓解在处理新图像或视频时必须唤醒多个应用程序时的性能和用户体验影响。Android 7.0(API级别24)扩展JobInfo并JobParameters提供了一种替代解决方案。

触发内容URI更改的作业
要触发内容URI更改的作业,Android 7.0(API级别24)JobInfo使用以下方法扩展API:

JobInfo.TriggerContentUri()
封装触发内容URI更改作业所需的参数。
JobInfo.Builder.addTriggerContentUri()

将TriggerContentUri对象传递给JobInfo。A ContentObserver 监视封装的内容URI。如果有多个TriggerContentUri对象与作业相关联,则即使系统仅报告其中一个内容URI的更改,系统也会提供回调。
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS如果给定URI的任何后代发生更改,则 添加该标志以触发该作业。该标志对应于notifyForDescendants传递给的参数registerContentObserver()。

注意: TriggerContentUri()不能与setPeriodic()或组合使用setPersisted()。要持续监视内容更改,请JobInfo在应用JobService完成处理最近的回调之前安排新的内容 。

以下示例代码计划作业,以便在系统报告对内容URI的更改时触发MEDIA_URI:

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
          (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo.Builder builder = new JobInfo.Builder(
          MY_BACKGROUND_JOB,
          new ComponentName(context, MediaContentJob.class));
  builder.addTriggerContentUri(
          new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
          JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
  js.schedule(builder.build());
}

当系统报告指定的内容URI(s)发生变化时,您的应用程序将收到一个回调,并将一个JobParameters对象传递给该onStartJob() 方法MediaContentJob.class。

确定哪些内容管理机构触发了一项工作

Android 7.0(API级别24)也扩展JobParameters为允许您的应用程序接收有关哪些内容权威机构和URI触发作业的有用信息:

Uri[] getTriggeredContentUris()
返回触发作业的URI数组。这将是null,如果没有任何的URI触发了作业(例如,作业被触发由于期限或其他原因),或改变的URI的数量大于50。
String[] getTriggeredContentAuthorities()
返回触发作业的内容权威机构的字符串数组。如果返回的数组不是null,则用于getTriggeredContentUris() 检索哪些URI已更改的详细信息。
以下示例代码将覆盖该JobService.onStartJob()方法并记录已触发作业的内容权限和URI:

@Override
public boolean onStartJob(JobParameters params) {
  StringBuilder sb = new StringBuilder();
  sb.append("Media content has changed:\n");
  if (params.getTriggeredContentAuthorities() != null) {
      sb.append("Authorities: ");
      boolean first = true;
      for (String auth :
          params.getTriggeredContentAuthorities()) {
          if (first) {
              first = false;
          } else {
             sb.append(", ");
          }
           sb.append(auth);
      }
      if (params.getTriggeredContentUris() != null) {
          for (Uri uri : params.getTriggeredContentUris()) {
              sb.append("\n");
              sb.append(uri);
          }
      }
  } else {
      sb.append("(No content)");
  }
  Log.i(TAG, sb.toString());
  return true;
}

进一步优化您的应用


优化您的应用程序以在低内存设备上或低内存条件下运行可以提高性能和用户体验。消除对后台服务和清单注册的隐式广播接收器的依赖可以帮助您的应用在这些设备上更好地运行。虽然Android 7.0(API级别24)采取措施来减少这些问题中的一些,但建议您优化应用程序以在完全不使用这些后台进程的情况下运行。

Android 7.0(API级别24)引入了一些额外的Android Debug Bridge(ADB)命令,您可以使用这些命令来测试禁用了这些后台进程的应用行为:

要模拟隐式广播和后台服务不可用的情况,请输入以下命令:

$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore

要重新启用隐式广播和后台服务,请输入以下命令:

$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
Lastest Update:2018.05.24

联系我

QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐:

【后台任务】后台优化(15)

猜你喜欢

转载自blog.51cto.com/4789781/2124442