引言
在鸿蒙应用实际开发中,经常会遇到App与IOT设备间的通信,本节主要详细讲述一下通信关键技术,考虑到TCP/UDP协议的特性,两者间通过UDP进行通信是一种必然的选择,UDP一种无连接的协议,具有资源消耗小,处理速度快的优点,了解UDP是怎么通信的,这对于每一个HarmonyOS开发者也是需要了解的重点知识。
核心类
DatagramSocket、DatagramPacket、EventHandler,下面分别简单介绍下:
1.DatagramSocket:
构造器DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。主要方法receive(DatagramPacket p):从该DatagramSocket中接收数据报,send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。
2.DatagramPacket:
构造器DatagramPacket(byte[] buf, int length, InetAddress addr, int port):以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口--这就决定了该数据报的目的地。
3.EventHandler:
是HarmonyOS用于处理线程间通信的一种机制,可以通过EventRunner创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用EventHandler创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过EventHandler通知主线程,主线程再更新UI。
功能介绍
通过App Demo控制小车运动(前进、后退、左转、右转、停止),主要通过UDP数据包发送命令,来说明它们间是怎么通信的,它们间控制命令以json格式发送。
如:
- "mode": "CarControl",//控制命令分类
- "cmd": "forward"//具体命令
- }。
开发指南
1、创建UDP协议的发送命令对象
- private UdpManager() {
- try {
- mGpsDatagramSocket = new DatagramSocket();
- } catch (SocketException e) {
- e.printStackTrace();
- }
- }
2、将要发送的数据封装成DatagramPacket对象发送
- DatagramPacket sRequest = new DatagramPacket(mInfoArray, mInfoArray.length,
- InetAddress.getByName(getIp()), PORT);
- // 开始发送
- mGpsDatagramSocket.send(sRequest);
3、构造发送的命令
- public void sendMessage(String info) {
- Gson gson = new Gson();
- WifiCommand messageInfo = new WifiCommand();
- messageInfo.setCmd(info);
- //控制类型
- messageInfo.setMode();
- //转换成json
- String resultJson = gson.toJson(messageInfo);
- // 创建发送命令SendMessageRunnable对象
- mSendMessageRunnable = new SendMessageRunnable();
- mSendMessageRunnable.setInfoArray(resultJson.getBytes(StandardCharsets.UTF_8));
- // 启动发送命令线程
- mEventHandler.postTask(mSendMessageRunnable);
- if ("stop".equals(info) || "tripod_on".equals(info) || "tripod_off".equals(info)){
- HiLog.info(label, "info = " + info);
- } else {
- // 启动发送Gps请求线程和接收信息线程
- startReceive();
- startSendGpsMessage();
- }
- HiLog.info(label, "sendMessage = " + resultJson);
- }
实现效果
附上主要源代码
1. MainAbilitySlice
- public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener{
- private Button iTurnUp,iTurnDown,iTurnLeft,iTurnRight,iTurnRun;
- private UdpManager udpManager;
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
- initComponent();
- // 初始化WiFi控制对象
- udpManager = UdpManager.getInstance(this);
- }
- private void initComponent(){
- iTurnUp = (Button) findComponentById(ResourceTable.Id_i_up);
- iTurnUp.setClickedListener(this);
- iTurnDown = (Button) findComponentById(ResourceTable.Id_i_down);
- iTurnDown.setClickedListener(this);
- iTurnLeft = (Button) findComponentById(ResourceTable.Id_i_left);
- iTurnLeft.setClickedListener(this);
- iTurnRight = (Button) findComponentById(ResourceTable.Id_i_right);
- iTurnRight.setClickedListener(this);
- iTurnRun = (Button) findComponentById(ResourceTable.Id_i_run);
- iTurnRun.setClickedListener(this);
2. UdpManager
- /**
- * UDP连接类
- */
- public class UdpManager {
- private static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00134, "UdpManager");
- private static final int PORT = 48100;
- private static final int GET_MESSAGE = 1;
- private static UdpManager sUdpManager;
- private static Context sContext;
- private UdpReceiveCallback mReceiveInformationCallback;
- private ReceiveMessageRunnable mReceiveMessageRunnable;
- private SendGpsMessageRunnable mSendGpsMessageRunnable;
- private SendMessageRunnable mSendMessageRunnable;
- private DatagramSocket mGpsDatagramSocket;
- private static String ip = "192.168.0.1";
- /**
- * 控制是否还需要接收信息控制器
- */
- private boolean flag = false;
- private final EventHandler mEventHandler = new EventHandler(EventRunner.create()) {
- @Override
- protected void processEvent(InnerEvent event) {
- super.processEvent(event);
- if (event.eventId == GET_MESSAGE) {
- if (mReceiveInformationCallback != null) {
- mReceiveInformationCallback.getMessage(event.object);
- }
- }
- }
- };
- private final EventHandler mReceiveEventHandler = new EventHandler(EventRunner.create()) {
- };
- private final EventHandler mSendGpsEventHandler = new EventHandler(EventRunner.create()) {
- };
- /**
- * UdpManager的单例
- *
- * @return UdpManager单例对象
- */
- public static UdpManager getInstance(Context context) {
- if (sUdpManager == null) {
- sUdpManager = new UdpManager();
- sContext = context;
- }
- return sUdpManager;
- }
- /**
- * 构造函数
- */
- private UdpManager() {
- // 创建UDP协议的发送命令对象
- try {
- mGpsDatagramSocket = new DatagramSocket();
- } catch (SocketException e) {
- e.printStackTrace();
- }
- }
- /**
- * 注册接收信息的回调函数
- *
- * @param callback 接收信息回调函数
- */
- public void registerCallback(UdpReceiveCallback callback) {
- mReceiveInformationCallback = callback;
- }
- /**
- * 对外提供的发送命令方法
- *
- * @param info 需要发送的命令
- */
- public void sendMessage(String info) {
- Gson gson = new Gson();
- UdpCommand messageInfo = new UdpCommand();
- // 传进来的控制命令
- messageInfo.setCmd(info);
- //控制类型
- messageInfo.setMode();
- //转换成json
- String resultJson = gson.toJson(messageInfo);
- // 创建发送命令SendMessageRunnable对象
- mSendMessageRunnable = new SendMessageRunnable();
- mSendMessageRunnable.setInfoArray(resultJson.getBytes(StandardCharsets.UTF_8));
- // 启动发送命令线程
- mEventHandler.postTask(mSendMessageRunnable);
- // 启动发送Gps请求线程和接收信息线程
- if ("stop".equals(info)) {
- HiLog.info(label, "info = " + info);
- } else {
- // 启动发送Gps请求线程和接收信息线程
- startReceive();
- startSendGpsMessage();
- }
- HiLog.info(label, "sendMessage = " + resultJson);
- }
- public String getIp() {
- return ip;
- }
- public void setIp(String mIp) {
- this.ip = mIp;
- }
- /**
- * 内部类,用作发送命令
- */
- private class SendMessageRunnable implements Runnable {
- private byte[] mInfoArray;
- void setInfoArray(byte[] infoArray) {
- mInfoArray = infoArray;
- }
- @Override
- public void run() {
- HiLog.info(label, "发送线程 = " + Thread.currentThread().getName());
- // 发送数据
- try {
- // 延时发送50毫秒,因为如果不延时会将小车卡死
- Thread.sleep(50);
- // 将要发送的数据封装成DatagramPacket对象
- DatagramPacket sRequest = new DatagramPacket(mInfoArray, mInfoArray.length,
- InetAddress.getByName(getIp()), PORT);
- // 开始发送
- mGpsDatagramSocket.send(sRequest);
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- HiLog.info(label, "sendMessage error");
- }
- }
- }
- /**
- * 内部类,用作接收命令
- */
- private class ReceiveMessageRunnable implements Runnable {
- @Override
- public void run() {
- try {
- while (flag) {
- byte[] buf = new byte[1024];
- DatagramPacket receiveDatagramPacket = new DatagramPacket(buf, buf.length);
- if (mGpsDatagramSocket != null && !mGpsDatagramSocket.isClosed()) {
- HiLog.info(label, "接收线程开始阻塞" + Thread.currentThread().getName());
- // 接收返回数据,会阻塞线程
- mGpsDatagramSocket.receive(receiveDatagramPacket);
- // 将得到的数据转成json
- String json = new String(receiveDatagramPacket.getData(), StandardCharsets.UTF_8);
- json = json.substring(json.indexOf("{"), json.lastIndexOf("}")+1);
- HiLog.info(label, "receiveMessage json = " + json);
- // 将对象发送给需要接收返回值的地方
- mEventHandler.sendEvent(InnerEvent.get(GET_MESSAGE, json));
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- HiLog.error(label, "receiveMessage error");
- }
- }
- }
- /**
- * 内部类,用作发送请求Gps命令
- */
- private class SendGpsMessageRunnable implements Runnable {
- @Override
- public void run() {
- Gson gson = new Gson();
- UdpCommand messageInfo = new UdpCommand();
- // 传进来的控制命令
- messageInfo.setCmd("getinfo");
- //控制类型
- messageInfo.setMode();
- //转换成json
- String resultJson = gson.toJson(messageInfo);
- byte[] infoArray = resultJson.getBytes(StandardCharsets.UTF_8);
- try {
- // 将要发送的数据封装成DatagramPacket对象
- DatagramPacket sRequest = new DatagramPacket(infoArray, infoArray.length,
- InetAddress.getByName(getIp()), PORT);
- // 开始发送
- mGpsDatagramSocket.send(sRequest);
- // 启动获取Gps命令线程
- mSendGpsEventHandler.postTask(mSendGpsMessageRunnable, 2000);
- HiLog.info(label, "发送gps");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 启动接收消息
- */
- private void startReceive() {
- if (!flag) {
- flag = true;
- // 创建接收命令ReceiveMessageRunnable对象
- mReceiveMessageRunnable = new ReceiveMessageRunnable();
- // 启动接收命令线程
- mReceiveEventHandler.postTask(mReceiveMessageRunnable);
- HiLog.info(label, "开启接收线程");
- }
- }
- /**
- * 开始获取gps点
- */
- private void startSendGpsMessage() {
- // 创建发送Gps命令SendGpsMessageRunnable对象
- if (mSendGpsMessageRunnable == null) {
- mSendGpsMessageRunnable = new SendGpsMessageRunnable();
- }
- // 启动获取Gps命令线程
- mSendGpsEventHandler.postTask(mSendGpsMessageRunnable);
- HiLog.info(label, "开启发送gps请求线程");
- }
3. UdpCommand
- class UdpCommand {
- // 控制命令:forward,back,left,right
- private String cmd;
- // 控制类型
- private String mode;
- public String getCmd() {
- return cmd;
- }
- void setCmd(String cmd) {
- this.cmd = cmd;
- }
- public String getMode() {
- return mode;
- }
- void setMode() {
- this.mode = "CarControl";
- }
- }
4. UdpReceiveCallback
- /**
- * 接收小车返回数据的回调函数
- */
- public interface UdpReceiveCallback {
- void getMessage(Object value);
- }
5. xml布局文件
- <?xml version="1.0" encoding="utf-8"?>
- <DirectionalLayout
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:alignment="center"
- ohos:orientation="vertical">
- <DirectionalLayout
- ohos:height="70vp"
- ohos:width="match_parent"
- ohos:orientation="horizontal"
- ohos:layout_alignment="center"
- ohos:top_margin="10vp" >
- <Button
- ohos:id="$+id:i_up"
- ohos:height="50vp"
- ohos:width="120vp"
- ohos:background_element="#FF9F9F9F"
- ohos:left_margin="60vp"
- ohos:text_size="25fp"
- ohos:text="前进"/>