当涉及到进行快速,简单的任务,可穿戴的应用有优势,作为一个智能手表这是正确的有你的手腕上总是会比智能手机或平板电脑,它的地方漂浮在你的包更容易。
但是,还没有完美的小工具,也没有人对智能手表的电池寿命赞不绝口,也没有人声称它的速度和功能与智能手机或平板电脑一样强大。
为了提供最佳的用户体验,您需要发挥设备的优势。 如果您正在开发Wear OS( 以前称为Android-Wear的操作系统 ),那么您将处于从两个截然不同的设备中挑选最佳功能的独特位置。
基本上,您可以两全其美!
在本文中,我将向您展示如何通过打开两者之间的沟通渠道,充分利用Android OS和Wear OS所提供的一切。 掌上电脑应用程序及其可穿戴设备聊天之后,您可以根据最适合的设备委派任务-是否将耗电的工作分担到掌上电脑上,或者通过显示应用程序来确保始终可以轻松访问应用程序最重要的信息在用户的手腕上。
到本文结尾,您将创建一个手持设备和可穿戴应用程序,它们可以通过Wearable Data Layer和MessageClient
API交换信息。
什么是可穿戴数据层?
可穿戴数据层提供对各种客户端类的访问,您可以使用这些客户端类来存储和检索数据,而不必费心处理诸如数据序列化之类的技术细节。 一旦此信息位于数据层上,手持设备和可穿戴设备均可访问。
在本文中,我们将重点介绍MessageClient
API,它是一种单向通信机制,可用于将信息发送到可穿戴数据层。 该API特别适用于执行远程过程调用(RPC),例如在配对的手持设备或可穿戴设备上远程启动Activity
。
让我们看一个例子:假设您已经创建了一个导航应用程序。 此应用需要a)检索位置更新,以及b)向用户提供指导。
监视设备的位置是一项繁重的任务,可以快速耗尽典型可穿戴设备可用的有限电池。 使用MessageClient
API,您的可穿戴应用程序可以指示其手持式对等设备执行此工作。 掌上电脑执行了此繁重的工作后,便可以通过数据层将结果信息发送回可穿戴设备,因此您的应用程序可以获取所需的信息, 而无需从可穿戴设备的剩余电池中取出任何一块。
通常,如果您的可穿戴式应用程序需要执行需要大量电池或处理能力或复杂的用户交互操作的任务,则应考虑将这项工作分流到相应的手持式应用程序。 相比之下,如果您的应用处理的是对时间特别敏感的信息,或者用户可能随时随地访问的内容,那么您应该在可穿戴式应用上显示此信息。
在我们的导航应用程序示例中,将每组方向从掌上电脑推到可穿戴设备,使这些信息更易于访问, 尤其是对于那些出门在外而无可救药的人!
开箱时, MessageClient
API是一个单向的沟通机制,但是你可以通过创建一个发送器和接收器实现双向通讯两个项目的手持设备和可穿戴式模块,而这正是我们要做的。
创建可穿戴和手持式模块
在本文中,我们将创建一个可穿戴式应用程序,该应用程序将在手持设备向数据层发送新消息时识别它。 然后,该可穿戴应用将通过检索此消息并将其显示为用户界面的一部分来做出响应。
然后,我们将进行冲洗和重复操作,创建一个手持应用程序,该应用程序监视数据层中可穿戴设备发送的消息。
通过MessageClient
API发送的信息只能由创建它的应用程序访问。 如果系统要识别您的可穿戴设备和手持设备属于同一应用程序,则它们将需要具有相同的软件包名称,版本代码和签名证书。 勾选所有这些框的最简单方法是创建一个由可穿戴模块和手持模块组成的项目:
- 创建一个名为DataLayer的新项目。
- 在“ 目标Android设备”屏幕上,选择手机和平板电脑和穿戴式设备 。 单击下一步 。
- 对于您的手机和平板电脑模块,选择清空活动模板,然后单击下一步 。
- 对于您的可穿戴模块,选择空白磨损活动模板,然后单击下一步 ,然后单击完成 。
创建您的手持式应用
由于我们正在实现双向通信,因此手持设备和移动模块都需要自己的侦听器和发送器。 让我们从在手持应用程序中实现此功能开始。
我将保持简单,并创建一个由TextView
组成的UI,该UI将显示从数据层检索到的各种消息,以及一个按钮,在点击该按钮时,它将自己的消息发送到数据层。
打开移动模块的activity_main.xml文件,然后添加以下内容:
由于我们引用了一些dimens.xml值,因此我们需要为这些值提供定义:
- 按住Control键并单击移动模块的res / values目录。
- 选择“ 新建”>“值”资源文件 。
- 将此文件命名为dimens.xml ,然后单击“ 确定” 。
- 添加以下内容:
20dp
20dp
这为我们提供了以下用户界面:
添加您的依赖
打开移动模块的build.gradle文件并添加以下依赖项:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
wearApp project(':wear')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.google.android.gms:play-services-wearable:+'
}
在MainActivity中显示和发送消息
在MainActivity
,我们需要执行以下操作:
让用户处于循环中!
当用户点击“ 交谈到可穿戴设备”按钮时,需要发生两件事:
- 手持设备向可穿戴设备发送消息。 我将使用“ 我从掌上电脑收到消息 ”。
- 手持设备提供视觉确认消息已成功发送。 我将使用“ 我向可穿戴设备发送了一条消息 ”。
当用户点击掌上电脑的“与可穿戴设备通话”按钮时,掌上电脑将尝试向数据层发送消息。 系统仅将该消息排队等待传递到特定设备后,才认为该消息已成功发送,这意味着至少需要有一对配对的设备可用。
在最佳情况下,用户点按“与可穿戴设备交谈” ,消息将排队等待发送,而我们的掌上电脑胜利地宣称:“ 我刚刚向可穿戴设备发送了一条消息 。”
但是,如果没有可用的可穿戴设备,则消息不会排队,并且默认情况下,用户无法确认我们的应用程序甚至尝试发送消息。 这可能会导致用户怀疑应用程序是否损坏,因此我还将显示“ 正在发送消息...” 。 通知,无论邮件是否成功排队。
在测试此应用时,您可能还希望快速连续触发多条消息。 为了清楚说明何时将每条消息排队等待传递,我在每条消息上添加了一个计数器,因此掌上电脑将显示我刚刚向可穿戴设备2 发送了消息 , 我刚刚向可穿戴设备3发送了消息 ,依此类推上。 在连接的另一侧,我们的可穿戴设备将显示我刚刚从手持设备2 收到消息 , 我刚刚从手持设备3收到消息 ,依此类推。
2.显示收到的消息
在下一节中,我们将创建一个MessageService
来监视数据层并检索消息。 由于我们的服务将在不同的线程上执行其工作,因此它将将此信息广播到我们的MainActivity
,后者将负责更新UI。
3.定义路径
您通过MessageClient
API传输的每条消息都必须包含一个路径,该路径是一个唯一标识该消息并允许您的应用从连接的另一端访问的字符串。
该路径始终以正斜杠开头(我正在使用/ my_path ),并且还可以包含可选的有效载荷,其形式为字节数组。
4.检查您的节点!
在Google Play服务7.3.0及更高版本中,您可以将多个可穿戴设备连接到单个手持设备上-例如,用户可能会在他们之间切换或同时使用的多个可穿戴设备上大吃一惊。 Wear OS设备还可能在其使用寿命期间连接到多个手持设备,例如,如果用户拥有Android智能手机和平板电脑,或者用闪亮的新智能手机替换了旧的智能手机。 请注意,任何能够连接到数据层的设备在应用程序代码中都称为节点 。
在本文中,我将假设只有一个可用的可穿戴设备。 另外,您可以使用GetConnectedNodes或getLocalNode来选择向哪些设备发送消息。
让我们在MainActivity
实现所有这些:
import android.support.v7.app.AppCompatActivity;
import android.content.BroadcastReceiver;
import android.widget.Button;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.google.android.gms.wearable.Wearable;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class MainActivity extends AppCompatActivity {
Button talkbutton;
TextView textview;
protected Handler myHandler;
int receivedMessageNumber = 1;
int sentMessageNumber = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
talkbutton = findViewById(R.id.talkButton);
textview = findViewById(R.id.textView);
//Create a message handler//
myHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Bundle stuff = msg.getData();
messageText(stuff.getString("messageText"));
return true;
}
});
//Register to receive local broadcasts, which we'll be creating in the next step//
IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
Receiver messageReceiver = new Receiver();
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, messageFilter);
}
public void messageText(String newinfo) {
if (newinfo.compareTo("") != 0) {
textview.append("\n" + newinfo);
}
}
//Define a nested class that extends BroadcastReceiver//
public class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//Upon receiving each message from the wearable, display the following text//
String message = "I just received a message from the wearable " + receivedMessageNumber++;;
textview.setText(message);
}
}
public void talkClick(View v) {
String message = "Sending message.... ";
textview.setText(message);
//Sending a message can block the main UI thread, so use a new thread//
new NewThread("/my_path", message).start();
}
//Use a Bundle to encapsulate our message//
public void sendmessage(String messageText) {
Bundle bundle = new Bundle();
bundle.putString("messageText", messageText);
Message msg = myHandler.obtainMessage();
msg.setData(bundle);
myHandler.sendMessage(msg);
}
class NewThread extends Thread {
String path;
String message;
//Constructor for sending information to the Data Layer//
NewThread(String p, String m) {
path = p;
message = m;
}
public void run() {
//Retrieve the connected devices, known as nodes//
Task> wearableList =
Wearable.getNodeClient(getApplicationContext()).getConnectedNodes();
try {
List nodes = Tasks.await(wearableList);
for (Node node : nodes) {
Task sendMessageTask =
//Send the message//
Wearable.getMessageClient(MainActivity.this).sendMessage(node.getId(), path, message.getBytes());
try {
//Block on a task and get the result synchronously//
Integer result = Tasks.await(sendMessageTask);
sendmessage("I just sent the wearable a message " + sentMessageNumber++);
//if the Task fails, then…..//
} catch (ExecutionException exception) {
//TO DO: Handle the exception//
} catch (InterruptedException exception) {
//TO DO: Handle the exception//
}
}
} catch (ExecutionException exception) {
//TO DO: Handle the exception//
} catch (InterruptedException exception) {
//TO DO: Handle the exception//
}
}
}
}
创建一个收听服务
在这一点上,我们的手持设备能够将消息推送到数据层,但是由于我们要实现双向通信,因此它还需要侦听到达数据层的消息。
在本节中,我们将创建一个执行以下操作的服务:
监视数据层中的事件
您可以通过实现DataClient.OnDataChangedListener
接口或扩展WearableListenerService
来监视数据层。 我选择后者,因为扩展WearableListenerService
有一些好处。 首先, WearableListenerService
在后台线程上进行工作,因此您不必担心会阻塞主UI线程。 其次,系统管理WearableListenerService
生命周期,以确保它不会消耗不必要的资源,无需根据需要绑定和取消绑定服务。
缺点是,即使您的应用程序未运行, WearableListenerService
也会侦听事件,并且如果检测到相关事件,它将启动您的应用程序。 如果您的应用仅在运行时才需要响应事件,则WearableListenerService
可能会不必要地消耗设备的电池。
2.覆盖相关的数据回调
WearableListenerService
可以侦听一系列数据层事件,因此对于需要处理的事件,您需要覆盖数据事件回调方法。 在我们的服务中,我实现了onMessageReceived
,它将在从远程节点发送消息时触发。
3.检查路径
每当消息发送到数据层时,我们的应用程序都需要检查消息是否具有正确的my_path
标识符。
4.广播消息到MainActivity
由于WearableListenerService
在不同的线程上运行,因此无法直接更新UI。 要在我们的应用程序中显示一条消息,我们需要使用LocalBroadcastManager
将其转发到MainActivity
。
要创建服务:
- 确保已选择移动模块。
- 从Android Studio工具栏中选择新建>服务 。
- 将此服务命名为
MessageService
。 - 添加以下内容:
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;
//Extend WearableListenerService//
public class MessageService extends WearableListenerService {
@Override
public void onMessageReceived(MessageEvent messageEvent) {
//If the message’s path equals "/my_path"...//
if (messageEvent.getPath().equals("/my_path")) {
//...retrieve the message//
final String message = new String(messageEvent.getData());
Intent messageIntent = new Intent();
messageIntent.setAction(Intent.ACTION_SEND);
messageIntent.putExtra("message", message);
//Broadcast the received Data Layer messages locally//
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
}
else {
super.onMessageReceived(messageEvent);
}
}
}
最后,打开Manifest
并将一些信息添加到MessageService
条目中:
//Add metadata for Google Play Services//
//Add the gms.wearable.MESSAGE_RECEIVED intent filter//
//Specify your path, and a host for the filter. Here, I’m using a wildcard host//
如前所述,系统仅在排队等待传递时才考虑成功发送消息,只有在一个或多个可穿戴设备可用时,才会发生该消息。
通过将移动模块安装在兼容的智能手机或平板电脑或Android虚拟设备(AVD)上,您可以看到实际的操作。 单击“ 与可穿戴设备交谈”按钮,该应用程序将显示“ 正在发送消息...”文本。 我刚刚发送了可穿戴设备……文本不会出现。
如果我们的消息要排队等待传递,那么我们需要在项目的可穿戴模块中实现另一组发送者和接收者组件。
创建您的可穿戴应用
我们的可穿戴应用程序将具有与手持设备类似的功能,因此我将跳过我们已经介绍的所有代码。
再一次,让我们从创建应用程序的用户界面开始。 打开磨损模块的activity_main.xml文件并添加以下内容:
//Wrap the layout in BoxInsetLayout so it displays correctly on both square and round watches//
此时,您的用户界面应如下所示:
打开build.gradle并添加以下依赖项:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
provided 'com.google.android.wearable:wearable:2.2.0'
implementation 'com.google.android.support:wearable:2.2.0'
implementation 'com.google.android.gms:play-services-wearable:12.0.1'
implementation 'com.android.support:wear:26.1.0'
}
现在,我们需要将消息发送到数据层:
import android.content.BroadcastReceiver;
import android.os.Bundle;
import android.widget.Button;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.wearable.activity.WearableActivity;
import android.widget.TextView;
import android.view.View;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.Node;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class MainActivity extends WearableActivity {
private TextView textView;
Button talkButton;
int receivedMessageNumber = 1;
int sentMessageNumber = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
talkButton = findViewById(R.id.talkClick);
//Create an OnClickListener//
talkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String onClickMessage = "I just sent the handheld a message " + sentMessageNumber++;
textView.setText(onClickMessage);
//Use the same path//
String datapath = "/my_path";
new SendMessage(datapath, onClickMessage).start();
}
});
//Register to receive local broadcasts, which we'll be creating in the next step//
IntentFilter newFilter = new IntentFilter(Intent.ACTION_SEND);
Receiver messageReceiver = new Receiver();
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, newFilter);
}
public class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//Display the following when a new message is received//
String onMessageReceived = "I just received a message from the handheld " + receivedMessageNumber++;
textView.setText(onMessageReceived);
}
}
class SendMessage extends Thread {
String path;
String message;
//Constructor for sending information to the Data Layer//
SendMessage(String p, String m) {
path = p;
message = m;
}
public void run() {
//Retrieve the connected devices//
Task> nodeListTask =
Wearable.getNodeClient(getApplicationContext()).getConnectedNodes();
try {
//Block on a task and get the result synchronously//
List nodes = Tasks.await(nodeListTask);
for (Node node : nodes) {
//Send the message///
Task sendMessageTask =
Wearable.getMessageClient(MainActivity.this).sendMessage(node.getId(), path, message.getBytes());
try {
Integer result = Tasks.await(sendMessageTask);
//Handle the errors//
} catch (ExecutionException exception) {
//TO DO//
} catch (InterruptedException exception) {
//TO DO//
}
}
} catch (ExecutionException exception) {
//TO DO//
} catch (InterruptedException exception) {
//TO DO//
}
}
}
}
接下来,我们需要创建一个监听器,该监听器将监视数据层中的传入消息,并在收到新消息时通知MainActivity:
- 确保已选择磨损模块。
- 从Android Studio工具栏中选择“ 新建”>“服务 ”。
- 将此服务命名为MessageService ,然后添加以下内容:
import android.content.Intent;
import com.google.android.gms.wearable.MessageEvent;
import android.support.v4.content.LocalBroadcastManager;
import com.google.android.gms.wearable.WearableListenerService;
public class MessageService extends WearableListenerService {
@Override
public void onMessageReceived(MessageEvent messageEvent) {
//If the message’s path equals "/my_path"...//
if (messageEvent.getPath().equals("/my_path")) {
//...retrieve the message//
final String message = new String(messageEvent.getData());
Intent messageIntent = new Intent();
messageIntent.setAction(Intent.ACTION_SEND);
messageIntent.putExtra("message", message);
//Broadcast the received Data Layer messages locally//
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
}
else {
super.onMessageReceived(messageEvent);
}
}
}
打开模块的Manifest
,并为WearableListenerService
创建一个意图过滤器:
//Add the gms.wearable.MESSAGE_RECEIVED intent filter//
//Specify your path, and a host for the filter. Again, I’m using a wildcard//
您可以从GitHub下载完整的项目 。
测试您的应用
此时,您有两个应用程序可以通过数据层交换消息,但是如果要测试这些通信技能,则需要在手持设备和可穿戴设备上安装项目。
如果您是Android开发人员,那么可能至少有一台Android智能手机或平板电脑摆在附近,但是可穿戴设备仍然感觉像是一种相对较新的利基产品,因此您可能尚未投资购买智能手表。
如果您确实决定继续进行Wear OS开发,那么您应该冒险并购买智能手表,因为没有替代品可以在真实的Android设备上测试您的应用程序。 但是,如果您只是尝试使用Wear OS,则可以创建模拟可穿戴设备的AVD,就像创建模拟智能手机或平板电脑的AVD一样。 然后,您可以使用端口转发让您的AVD和您的物理Android设备对话。
第一步是创建可穿戴的AVD并将您的磨损模块安装在此仿真设备上:
- 从Android Studio工具栏中选择工具> Android> AVD管理器 。
- 单击创建虚拟设备...
- 从左侧菜单中选择磨损 。
- 选择您要模拟的可穿戴设备,然后单击下一步 。
- 选择系统映像,然后单击下一步 。
- 为您的AVD命名,然后单击Finish 。
- 从Android Studio工具栏中选择运行>运行...。
- 在出现的小弹出窗口中,选择Wear…
- 选择您刚刚创建的可穿戴AVD。 片刻之后,AVD将启动,并且您的可穿戴组件已经安装。
接下来,在智能手机或平板电脑上安装手持模块:
- 将您的物理Android设备连接到开发计算机。
- 从Android Studio工具栏中选择运行>运行...。
- 出现提示时选择手机 。
最后,我们需要让我们的物理Android设备和我们的AVD对话:
- 确保掌上电脑已启用蓝牙( “设置”>“蓝牙” ),并且已通过USB电缆将其连接到开发计算机。
- 在您的手持设备上,打开Play商店,然后下载Google应用(以前称为Android Wear)的Wear OS 。
- 启动Wear OS应用程序。
- 在模拟的可穿戴设备上,单击随附的按钮条中的“ 主页 ”按钮(在下面的屏幕快照中,光标位于其中),然后打开“ 设置”应用程序。
- 选择系统>关于 ,然后反复单击内部版本号 ,直到看到“ 您现在是开发人员”消息。
- 单击两次“ 返回”按钮返回到“ 设置 ”主菜单。 您应该注意到一个新的“ 开发人员选项”项; 点击一下。
- 选择“ ADB调试” 。
- 在开发计算机上,打开一个新的命令提示符(Windows)或终端(Mac),然后更改目录 (
cd
),使其指向Android SDK的platform-tools文件夹。 例如,我的命令如下所示:
cd /Users/jessicathornsby/Library/Android/sdk/platform-tools
- 通过运行
/.adb devices
命令,确保ADB(Android调试桥)能够识别模拟器和您所连接的智能手机或平板电脑。 它应返回两个单独设备的代码。 - 通过在“终端/命令提示符”窗口中运行以下命令,将AVD的通信端口转发到连接的智能手机或平板电脑:
./adb -d forward tcp:5601 tcp:5601
- 在掌上电脑上,启动Wear OS应用。 浏览所有介绍性对话,直到您进入Wear OS主屏幕。
- 打开左上角的下拉菜单,然后选择添加新手表 。
- 点击右上角的虚线图标,然后选择与仿真器配对 。 片刻之后,掌上电脑应连接到仿真器。
您现在可以测试您的应用了! 在模拟器上启动Wear组件,在掌上电脑上启动移动组件,然后通过点击不同的Talk ...按钮进行实验。
当您点击掌上电脑上的“与便携式设备通话”时 ,将出现以下消息:
- 掌上电脑: “我刚刚向掌上电脑发送了一条消息。”
- 可穿戴式: “我刚从掌上电脑收到一条消息。”
当您点击可穿戴设备上的与掌上电脑通话时,将出现以下消息:
- 可穿戴: “我刚刚向掌上电脑发送了一条消息。”
- 掌上电脑: “我刚从可穿戴设备收到一条消息。”
结论
在本文中,我们研究了如何通过可穿戴数据层在掌上电脑和可穿戴应用之间交换消息。
在生产中,您可能会使用这种技术来做一些有趣的事情,而不仅仅是交换相同的几行文本! 例如,如果您开发了一个在用户的智能手机上播放音乐的应用,则可以通过在数据层上将这些指示从可穿戴设备发送到手持设备,使他们能够直接从其可穿戴设备播放,暂停和跳过歌曲。
您可以通过Android官方文档了解有关可穿戴数据层的更多信息,包括如何同步更复杂的数据。
翻译自: https://code.tutsplus.com/tutorials/get-wear-os-and-android-talking-exchanging-information-via-the-wearable-data-layer--cms-30986