Android中通过USB接口与和PC进行通讯的demo程序

mac2024-10-23  2

以往开发过多款安卓嵌入式设备,这些设备与PC通讯主要通过设备上的以太网网口进行网络通讯,最近一个项目设备没有以太网网口,设备与PC通讯要求使用普通安卓数据线连接设备与PC完成数据通讯。

查阅相关资料以及对adb操作指令的了解,最终实现了项目需求。

方案实现思路:

1.设备端开启一个socket服务,服务端口12345;

2.设备端监听USB插拔事件来启动关闭设备端的socket服务;

3.PC端使用adb命令:adb forward tcp:54321 tcp:12345,将PC端口54321上发来的数据转换到设备socket服务端口;

4.PC端启动socket客户端与PC端口54321进行数据通讯。

因为项目代码保密,故对方案实现特意写了一个demo程序。

Demo实现了:

1.安卓设备端socket服务,服务监听端口12345,收到任何消息,均回复设备的毫秒时间戳。

2.PC 实现使用adb命令将PC端口54321消息转发到设备服务端口12345,之后,连接socket连接,并监听端口54321的消息,每隔3s发送PC毫秒时间戳,并监听设备端服务回应的数据。

以下以代码进行进行介绍。

Android端:

package com.yx.usb2pc; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.util.Log; /** * UsbConnectReceiver * 主要监听ACTION_USB_STATE,启动关闭socket服务 * * @author yx * @date 2019/10/30 20:39 */ public class UsbConnectReceiver extends BroadcastReceiver { private static final String TAG = "UsbConnectReceiver"; @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra(UsbManager.EXTRA_PERMISSION_GRANTED)) { boolean permissionGranted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); Log.v(TAG, "permissionGranted : " + permissionGranted); } String action = intent.getAction(); Log.v(TAG, "action:" + action); switch (action) { case UsbManager.ACTION_USB_ACCESSORY_ATTACHED: case UsbManager.ACTION_USB_ACCESSORY_DETACHED: UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); Log.v(TAG, accessory.toString()); break; case UsbManager.ACTION_USB_DEVICE_ATTACHED: Log.v(TAG, UsbManager.ACTION_USB_DEVICE_ATTACHED); break; case UsbManager.ACTION_USB_DEVICE_DETACHED: UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); Log.v(TAG, device.toString()); break; case UsbManager.ACTION_USB_STATE: boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); boolean functionAdb = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false); Log.v(TAG, "connected:" + connected + " function_adb : " + functionAdb); // 断开停止服务 if (!connected) { SocketServer.getSocketServerInstant().stopServer(); } else { // 开启adb调试功能,开启服务 if (functionAdb) { SocketServer.getSocketServerInstant().startServer(); } } break; default: break; } } } UsbConnectReceiver,广播接收器,监听UsbManager.ACTION_USB_STATE状态变化,如果连接断开(connected==false),关闭设备端的socket服务,当然已连接上需要判断adb功能是否使能才能开启服务。 package com.yx.usb2pc; import android.util.Log; import com.yx.usb2pc.utils.ThreadPoolManager; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.ServerSocket; import java.net.Socket; /** * SocketServer * 服务端接收socket信息,回复设备端的时间戳信息 * * @author yx * @date 2019/10/30 20:39 */ public class SocketServer { private static final String TAG = "SocketServer"; private static final int PORT = 12345; private ServerSocket mServerSocket = null; private boolean mRunning = false; private static SocketServer sSocketServer; public static SocketServer getSocketServerInstant() { if (sSocketServer == null) { sSocketServer = new SocketServer(); } return sSocketServer; } private SocketServer() { } /** * 启动服务 */ public void startServer() { stopServer(); ThreadPoolManager.getInstance().startTaskThread(new ServerThread(), "server-thread"); } /** * 关闭服务 */ public void stopServer() { if (mServerSocket != null) { mRunning = false; try { mServerSocket.close(); Log.d(TAG, "stopServer server "); } catch (Exception e) { e.printStackTrace(); } mServerSocket = null; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } class ServerThread extends Thread { @Override public void run() { mRunning = true; try { mServerSocket = new ServerSocket(PORT); Log.d(TAG, "start server port = " + PORT); while (mRunning) { Socket socket = mServerSocket.accept(); Log.d(TAG, "accept "); ThreadPoolManager.getInstance() .startTaskThread(new ReceiveThread(socket), "receive-thread"); } } catch (Exception e) { e.printStackTrace(); } finally { if (mServerSocket != null) { try { mServerSocket.close(); } catch (Exception e) { e.printStackTrace(); } } } } } class ReceiveThread extends Thread { private Socket socket; public ReceiveThread(Socket socket) { this.socket = socket; Log.d(TAG, " socket:" + socket.toString()); } @Override public void run() { try { DataInputStream dis = new DataInputStream(socket.getInputStream()); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); while (true) { String data = dis.readUTF(); Log.d(TAG, "receive:" + data); // 接收到后回复设备的毫秒时间戳 String s = "device:" + System.currentTimeMillis(); dos.writeUTF(s); dos.flush(); } } catch (Exception e) { e.printStackTrace(); } finally { try { socket.close(); } catch (Exception e) { e.printStackTrace(); } } } } } SocketServer设备端socket服务,主要提供服务开启关闭方法,服务开启后ServerThread会循环侦听数据消息,每侦听到消息开启一个ReceiveThread线程处理数据,demo中数据接收到后回复设备的毫秒时间戳。

PC端:

package com.yx.soclectclient; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; /** * SocketClient * <p> * PC端每隔3s给监听端口发送PC时间戳信息 * * @author yx * @date 2019/5/17 13:57 */ public class SocketClient { private Socket mSocket = null; private static final int PORT = 54321; /** * adb 命令绑定端口 * * @return */ private boolean adbForward() { String cmd = "adb forward tcp:54321 tcp:12345"; try { Runtime.getRuntime().exec(cmd); } catch (IOException e) { e.printStackTrace(); } Process process = null; try { process = Runtime.getRuntime().exec(cmd); process.waitFor(); return true; } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (process != null) { process.destroy(); } } return false; } private void test() { try { mSocket = new Socket("127.0.0.1", PORT); System.out.println("socket:" + mSocket.toString()); DataInputStream dis = new DataInputStream(mSocket.getInputStream()); DataOutputStream dos = new DataOutputStream(mSocket.getOutputStream()); while (true) { String data = "sendTime:" + System.currentTimeMillis(); dos.writeUTF(data); dos.flush(); String s = dis.readUTF(); System.out.println("receive:" + s); Thread.sleep(3000); } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { SocketClient client = new SocketClient(); if (client.adbForward()) { client.test(); } } } SocketClient为PC端测试类,主要完成adb命令完成端口绑定,建立socket客户端连接,连接指向端口54321。连接后,每隔3s向端口发送PC时间戳,并侦听端口数据恢复。

ps:

1.本demo代码,android sdk版本为25;

2.因为测试时连接相对稳定,故未考虑socket长连接心跳问题及断线重连问题,可靠性应用需考虑心跳及断线重连问题。

3.PC端需要按照adb环境,使用时android设备需要打开adb调试模式。

4.USB数据线模拟网线进行socket通讯关键点是利用adb端口绑定命令:adb forward tcp:[pc port] tcp:[device port]。

5.demo源码:https://github.com/TomcatXiong/UsbPcCommunicationDemo

扫描关注更精彩

 

 

最新回复(0)