Save the date! Android Dev Summit is coming to Mountain View, CA on November 7-8, 2018.

网络访问和同步

有了 Android Wear 2.0,手表无需访问 Android 或 iOS 手机,即可直接与网络通信。这种直接网络访问取代了使用 Data Layer API(Wear 1.x 中)连接网络。

网络访问

Android Wear 应用可进行网络请求。当手表可通过蓝牙连接到手机时,手表的网络流量通常由手机代理。当手机不可用时,可根据硬件使用 WLAN 和蜂窝网络。Wear 平台可处理网络之间的转换。

您可以使用 HTTP、TCP 和 UDP 等协议。不过,不能使用 android.webkit API(包括 CookieManager 类)。您可以通过读取和写入请求和响应的标题来使用 Cookie。

此外,我们建议使用以下 API:

  • JobScheduler API,适用于异步作业,包括按定期间隔进行轮询(如下文所述)
  • 多网络 API,如果您需要连接至特定网络类型;请参阅多网络连接

高带宽网络访问

Android Wear 平台管理网络连接的目标是提供最佳的整体用户体验。此平台通过平衡以下两个因素来选择默认活动网络:

  • 是否需要持久的电池续航时间
  • 是否需要网络带宽

当节省电池电量优先时,活动网络可能没有足够的带宽来执行需要高带宽的网络任务,如传输大型文件或流媒体。

本部分将指导您如何使用 ConnectivityManager 类以确保为应用提供必需的网络带宽。如需有关精细控制网络资源的一般信息,请参阅管理网络使用情况

另请参阅下文所述的演示做法的示例

获取高带宽网络

在 Android Wear 上,请勿假设高带宽网络始终可用。对于需要高带宽网络访问的用例,如传输大型文件或流媒体,我们建议采取以下步骤:

  1. 检查活动网络,如果有活动网络,则检查其带宽。
  2. 如果没有活动网络或其带宽不足,则请求访问无限流量的 WLAN 或蜂窝网络。

您可以使用 ConnectivityManager 类检查是否存在活动网络以及是否具有足够的带宽:

int MIN_BANDWIDTH_KBPS = 320;
mConnectivityManager =
  (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Network activeNetwork = mConnectivityManager.getActiveNetwork();

if (activeNetwork != null) {
  int bandwidth =
    mConnectivityManager.getNetworkCapabilities(activeNetwork).getLinkDownstreamBandwidthKbps();

  if (bandwidth < MIN_BANDWIDTH_KBPS) {
    // Request a high-bandwidth network
  }
} else {
  // You already are on a high-bandwidth network, so start your network request
}

您可以使用 ConnectivityManager 请求无限流量的高带宽网络。对于单次网络请求,您可以请求无限流量的 WLAN 或蜂窝网络。当网络准备就绪时(例如,设备的 WLAN 无线装置连接至保存的网络),调用 NetworkCallback 实例的 onAvailable() 函数。如果未找到适合的网络,则不会调用 onAvailable() 函数。因此,您应手动将您的请求设置为超时;请参阅等待网络可用

mNetworkCallback = new ConnectivityManager.NetworkCallback() {
  @Override
  public void onAvailable(Network network) {
    if (bindProcessToNetwork(network)) {
      // socket connections will now use this network
    } else {
      // app doesn't have android.permission.INTERNET permission
    }
  }
};

NetworkRequest request = new NetworkRequest.Builder()
  .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
  .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
  .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
  .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
  .build();

mConnectivityManager.requestNetwork(request, mNetworkCallback);

释放网络

当您的应用不再需要高带宽网络时,必须使用 ConnectivityManager 类释放网络以确保平台可以恢复网络访问的管理。

mConnectivityManager.bindProcessToNetwork(null);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);

为了优化电池消耗,网络连接应仅在 Activity 持续时间保持注册状态。因此,您应考虑在 Activity 的 onStop() 函数中释放网络。

等待网络可用

可能无法瞬间获取网络,因为手表的 WLAN 或蜂窝无线装置可能处于关闭状态以节省电池。此外,如果手表无法连接至网络,则不会调用 NetworkCallback 实例的 onAvailable() 函数。因此,在预先确定的时间长度后,您应将请求设置为超时并释放所有关联资源。

int MESSAGE_CONNECTIVITY_TIMEOUT = 1;
long NETWORK_CONNECTIVITY_TIMEOUT_MS = 10000

mHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case MESSAGE_CONNECTIVITY_TIMEOUT:
        // unregister the network
        break;
    }
  }
};

mNetworkCallback = new ConnectivityManager.NetworkCallback() {
  @Override
  public void onAvailable(Network network) {
    mHandler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT);
    ...
  }
};

mConnectivityManager.requestNetwork(request, mNetworkCallback);

mHandler.sendMessageDelayed(
  mHandler.obtainMessage(MESSAGE_CONNECTIVITY_TIMEOUT),
  NETWORK_CONNECTIVITY_TIMEOUT_MS);

监控网络状态

NetworkCallback 接口具有用于监控所绑定网络的状态变化(如带宽发生变化和连接丢失)的函数。

mNetworkCallback = ConnectivityManager.NetworkCallback {
  @Override
  public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
    int bandwidth =
      mConnectivityManager.getNetworkCapabilities(network).getLinkDownstreamBandwidthKbps();

      if (bandwidth < MIN_BANDWIDTH.KBPS) {
        // handle insufficient network bandwidth
      }
  }

  @Override
  public void onLost(Network network) {
    // handle network loss
  }
}

启动 WLAN 设置 Activity

请求 WLAN 网络时,系统尝试连接至已保存的网络,前提是保存的网络已配置且在范围之内。不过,如果没有可用的已保存 WLAN 网络,则系统绝不会调用 NetworkCallback 实例的 onAvailable() 回调函数。如果您使用 Handler 将网络请求设置为超时,那么,在出现超时时,您可以指示用户添加一个 WLAN 网络。您可以直接将用户转到 Activity,以使用以下 Intent 添加 WLAN 网络:

context.startActivity(new Intent("com.google.android.clockwork.settings.connectivity.wifi.ADD_NETWORK_SETTINGS"));

要启动设置 Activity,您的应用必须具有以下权限:android.permission.CHANGE_WIFI_STATE

界面注意事项

如果应用需要连接至新的 WLAN 网络以进行高带宽操作,请确保在启动 WLAN 设置之前先向用户清晰阐明连接的原因。仅在需要高带宽网络时才请求用户添加新的 WLAN 网络。请勿阻止用户访问不需要高带宽网络的应用功能。

例如,图 1 显示的是一个音乐应用。此应用应允许用户浏览音乐,且仅在用户需要下载或流式传输音乐时才要求用户添加新的 WLAN 网络。

音乐下载

图 1. 用于下载音乐的音乐应用流程。

如果您的应用需要高带宽网络才能运行,在要求用户添加新的 WLAN 网络前,应先向用户阐述明确的理由。另外,对于长时间运行的网络操作,如下载用户的媒体播放列表,您应显示一个进度指示器,说明正在执行的操作。

图 2 显示的是音乐应用的流式传输音乐流程。如果用户想要流式传输音乐并且需要高带宽网络,在将用户转到 WLAN 设置之前,应用应清晰阐述为什么需要新的 WLAN 网络。

音乐流式传输

图 2. 用于流式传输音乐的音乐应用流程。

云消息传递

对于发送通知,应用可以直接使用 Firebase 云消息传递 (FCM),其取代了 Google Cloud Messaging (GCM)。Wear 2.0 支持 FCM,但不支持 GCM。

没有特定于 Android Wear 的网络访问 API 或 FCM。请参阅有关连接至网络云消息传递的现有文档。

FCM 在低电耗模式下能够顺畅运行,因此,建议通过 FCM 将通知发送到手表。

在 Wear 应用运行时,通过收集设备的注册令牌从 FCM 提供消息。然后,当服务器将消息发送至 FCM REST 端点时,添加令牌作为目的地的一部分。FCM 将消息发送至由令牌标识的设备。

FCM 消息采用 JSON 格式,并且可以包含以下一个或两个负载:

  • 通知负载。当手表收到通知负载时,直接在通知卡片信息流中向用户显示数据。当用户点按通知时,将启动您的应用。
  • 数据负载。数据负载具有一组自定义键值对。此负载将作为数据传输到 Wear 应用。

如需了解详细信息和负载示例,请参阅关于 FCM 消息

默认情况下,从手机应用将通知桥接(共享)到手表。如果您具有独立的 Wear 应用和对应的手机应用,则可能会出现重复的通知。例如,手机和手表从 FCM 接收的相同通知可单独在这两个设备上显示。

使用后台 Service

要确保后台任务得到正确执行,必须考虑到低电耗模式。在 Android 6.0 中,低电耗模式和应用待机模式可增加电池续航时间。

在 Android Nougat 和 Android Wear 2.0 中对低电耗模式进行了增强。当屏幕关闭或进入微光模式的时间足够长时,设备在特定时间段会部分进入低电耗模式,且后台任务可能会延迟。之后,如果设备长时间处于静止状态,则将进入常规低电耗模式。

您应使用 JobScheduler API 计划作业,您的应用可通过它注册在低电耗模式下安全的代码执行。计划作业时,您可以选择约束条件,如定期执行、需要连接或设备充电。采用不会对电池续航时间产生不利影响的方式配置作业。作业应使用 JobInfo.Builder 对象提供约束条件和元数据,例如,针对任务使用以下一个或多个函数:

  • 计划需要网络的任务时,请使用 setRequiredNetworkType(int networkType),从而指定 NETWORK_TYPE_ANYNETWORK_TYPE_UNMETERED;请注意,NETWORK_TYPE_UNMETERED 用于传输大型数据,而 NETWORK_TYPE_ANY 则用于传输小型数据。
  • 要在充电时计划任务,请使用 setRequiresCharging(boolean requiresCharging)
  • 要指定设备针对任务处于空闲状态,请使用 setRequiresDeviceIdle(boolean requiresDeviceIdle);此函数适用于低优先级的后台任务或同步,特别是在与 setRequiresCharging 一起使用时。

请注意,Bluetooth LE 等一些低带宽网络被视为按流量计费的网络。

使用约束条件进行计划

您可以计划需要约束条件的任务。在以下示例中,当符合以下约束条件时,JobScheduler 对象将激活 MyJobService

  • 无限流量网络
  • 设备充电

您可以使用构建器函数 setExtras 将一个应用特定的元数据捆绑包附加到作业请求中。在执行作业时,为作业 Service 提供此捆绑包。请注意传递到 JobInfo.Builder 构造函数的 MY_JOB_ID 值。这个 MY_JOB_ID 值是应用提供的标识符。取消后续调用并使用同一个值创建后续作业,这将更新现有作业:

JobInfo jobInfo = new JobInfo.Builder(MY_JOB_ID,
        new ComponentName(this, MyJobService.class))
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
        .setRequiresCharging(true)
        .setExtras(extras)
        .build();
((JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE))
        .schedule(jobInfo);

下面是 JobService 的一个实现,用于处理上述作业。执行作业时,将 JobParameters 对象传递到 onStartJob 函数。在计划作业时,您可以通过 JobParameters 对象获取作业 ID 值以及提供的任何额外的 extra。将会在主应用线程上调用 onStartJob 函数,因此,任何开销大的逻辑都应从其他线程运行。在此示例中,使用 AsyncTask 在后台运行代码。完成工作后,您应调用 jobFinished 函数以通知 JobScheduler 任务已完成:

public class MyJobService extends JobService {
    @Override public boolean onStartJob(JobParameters params) {
        new JobAsyncTask().execute(params);
        return true;
    }

    private class JobAsyncTask extends AsyncTask