基于HarmonyOS控制Hi3861小车之信息通信 原创 精华
引言
在鸿蒙应用实际开发中,经常会遇到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="前进"/>
感谢分享,回去试的跑一下。
软通同事文章写的越来越好了
感谢软通同事分享
感谢大家的支持
加油加油
感谢老师分享
前几天收到一个3861小车,试着用手机跑一下