首页 > 代码库 > 后台优化

后台优化

**本文章为翻译官方文档,如有错误,请指正**

后台优化

  后台进程可能是内存和电量集中型的。比如,一个隐式广播可能启动很多已经注册监听它的后台进程,即使这些进程可能什么事情都没做。这可能对设备性能和用户体验产生重大影响。

  为缓解这个问题,Android7.0(API级别24)会应用以下限制:

  • 针对Android7.0(API级别24)和更高版本的应用程序,假如在manifest中描述广播接收者,则不会接收到CONNECTIVITY_ACTION广播。假如通过Context.registerReceiver()注册广播接收者并且上下文依旧有效,则依然可以接收到CONNECTIVITY_ACTION广播。
  • 应用程序不能发送或者接收ACTION_NEW_PICTURE或者ACTION_NEW_VIDEO广播。这个优化对所有应用程序有效,不仅仅是针对Android7.0(API级别24)的应用。

  假如你的应用程序使用这些intents中的任何一个。你应该尽快移除对它们的依赖,如此你可以在目标设备上正确的运行Android7.0程序。Android框架提供几种解决办法,以减轻对这些隐式广播的需要。例如,JobScheduler和GcmNetworkMananger提供强大的机制来调度网络操作,在指定条件,如与未计量网络的连接被满足时。你现在也可以使用JobScheduler对内容提供者改变做出反馈。JobInfo对象封装了JobScheduler调度你的任务时使用到的参数。当任务条件被满足,系统在你应用的JobService上执行这个任务。

  在这篇文章中,我们将学习如何使用替代方法。如JobScheduler,以使你的应用适应这些新的限制。

CONNECTIVITY_ACTION的限制

  目标Android7.0(API级别24)的应用程序将不会接收到CONNECTIVITY_ACTION广播,假如在manifest中注册接收它们,并且依赖此种广播的进程将不会开启。这可能会在设备连接到未计量网络时,想监听网络变化或者执行批量网络操作的应用程序出现问题。绕过这个限制的几种解决方案已经存在Android框架中,但是选择正确的方案依赖于你希望你的应用程序完成什么。

注意:通过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);
}

  当你作业的条件被满足的时候,你的应用程序接收一个回调,以在特定JobService.class中运行onStartJob()方法。想看更多实现JobScheduler接口的例子,请看JobScheduler示例程序。

  使用GMSCore服务的应用程序和目标Android 5.0(API级别21)或更低版本,可以使用GcmNetworkManager并指定Task.NETWORK_STATE_UNMETERED。

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

  运行着的应用程序依旧可以通过注册一个广播BroadcastReceiver监听CONNECTIVITY_CHANGE。然而,ConnectivityManager提供一个更加强大的方法,仅仅当特定网络条件被满足的时候才请求回调。

  NetworkRequest对象根据NetworkCapabilities定义网络回调的参数。你通过NetworkRequest.Builder类创建NetworkRequest对象。调用registerNetworkCallback()方法将NetworkRequest对象传递给系统。当网络条件被满足的时候,应用程序接收一个回调,以执行定义在ConnectivityManager.NetworkCallback类中的onAvailable()方法。

  应用程序依旧可以接收回调除非退出应用程序或者调用unregisterNetworkCallback()。

NEW_PICTURE和NEW_VIDEO的限制

  在Android7.0(API级别24),应用程序不能发送或接收ACTION_NEW_PICTURE和ACTION_NEW_VIDEO广播。这个限制能缓解当几个应用程序唤醒以处理新图片或者视频时的性能和用户体验影响。Android7.0(API级别24)扩展了JobInfo和JobParameters以实现替代方案。

新JobInfo方法

  要在内容URI改变中触发作业,Android7.0(API级别24)使用以下方法扩展JobInfo API:

  JobInfo.TriggerContentUri()

    封装在内容URI更改时触发作业所需的参数

  JobInfo.Builder.addTriggerContentUri()

    传递TriggerContentUri对象给JobInfo.ContentObserver监视封装内容的URI。假如有多个TriggerContentUri被一个作业关联,则系统提供回调,即使它报告只有一个内容URI改变。

    添加TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS标记以触发作业,假如任何子类给定URI改变。这个标记对应传递给registerContentObserver()的 notifyForDescendants参数。

注意:TriggerContentUri()不能与setPeriodic()或setPersisted()组合使用。为了持续监视内容的更改,请在程序JobService完成处理最近的回调之前调度一个新的JobInfo。

  以下示例代码调度作业以在系统报告内容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改变的时候,你的应用程序接收一个回调并传递一个JobParameters对象到MediaContentJob类的onStartJob()方法中。

新的JobParameter方法

  Android7.0(API层级24)也扩展JobParameters以允许你的应用接收那些有关内容权限和URI触发作业的有用信息:

  Uri[] getTriggeredContentUris()

    返回已经触发作业的URI数组。如果没有URI已经触发作业(例如,该作业由于截止日期或者其它一些原因而触发)或者改变URI的数量大于50,将返回null。

  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;
}

进一步优化你的应用

  优化你的应用程序以能在低内存设备或者低内存条件下运行,能提升性能和用户体验。移除依赖的后台服务和在manifest上注册的隐式广播接收器可以帮助你的应用在这样的设备上运行的更好。虽然Android7.0(API层级24)采取措施以减少其中的一些问题,建议你优化你的应用程序,而完全不使用这些后台进程。

  Android7.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

  

官方源地址:https://developer.android.google.cn/topic/performance/background-optimization.html

后台优化