基于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="前進"/>