Unity3D开发安卓APP实现PC USB数据接收方案求助
Got it, let's tackle this problem head-on. The issue with System.IO.Ports on Android makes total sense—it's a desktop-focused API that doesn't play nice with Android's USB subsystem. Here are three solid, actionable solutions to get your Unity Android app receiving data from a PC over USB:
1. Android USB Host API + Unity Native Plugin
Android's built-in USB Host API lets your app directly interact with USB devices when the device is in host mode. You'll need to build a small Android plugin in Android Studio, then bridge it to Unity.
Step 1: Add Required Permissions
Add these permissions to your AndroidManifest.xml (either in your Android Studio plugin project or Unity's Plugins/Android folder):
<uses-feature android:name="android.hardware.usb.host" android:required="true" /> <uses-permission android:name="android.permission.USB_PERMISSION" />
Step 2: Build the Android Plugin
Create a Java class to handle USB detection, permission requests, and data reading. Here's a simplified example:
package com.yourcompany.usbplugin; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.util.Log; import com.unity3d.player.UnityPlayer; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; public class USBCommunication { private static final String ACTION_USB_PERMISSION = "com.yourcompany.usbplugin.USB_PERMISSION"; private UsbManager usbManager; private InputStream inputStream; private Thread readThread; private boolean isReading = false; public USBCommunication() { usbManager = (UsbManager) UnityPlayer.currentActivity.getSystemService(Context.USB_SERVICE); registerPermissionReceiver(); } private void registerPermissionReceiver() { BroadcastReceiver permissionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (device != null) openDevice(device); } else { Log.d("USBPlugin", "Permission denied for device " + device); } } } } }; UnityPlayer.currentActivity.registerReceiver(permissionReceiver, new IntentFilter(ACTION_USB_PERMISSION)); } public void findAndRequestDevice(int vendorId, int productId) { HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); for (UsbDevice device : deviceList.values()) { if (device.getVendorId() == vendorId && device.getProductId() == productId) { PendingIntent permissionIntent = PendingIntent.getBroadcast(UnityPlayer.currentActivity, 0, new Intent(ACTION_USB_PERMISSION), 0); usbManager.requestPermission(device, permissionIntent); break; } } } private void openDevice(UsbDevice device) { try { UsbDeviceConnection connection = usbManager.openDevice(device); if (connection != null && connection.claimInterface(device.getInterface(0), true)) { inputStream = connection.openInputStream(0); startReading(); } } catch (IOException e) { Log.e("USBPlugin", "Error opening device: " + e.getMessage()); } } private void startReading() { isReading = true; readThread = new Thread(() -> { byte[] buffer = new byte[1024]; int bytesRead; try { while (isReading && inputStream != null) { bytesRead = inputStream.read(buffer); if (bytesRead > 0) { String data = new String(buffer, 0, bytesRead); UnityPlayer.UnitySendMessage("USBReceiver", "OnDataReceived", data); } } } catch (IOException e) { Log.e("USBPlugin", "Error reading data: " + e.getMessage()); } }); readThread.start(); } public void stopReading() { isReading = false; try { if (inputStream != null) inputStream.close(); } catch (IOException e) { Log.e("USBPlugin", "Error closing stream: " + e.getMessage()); } } }
Step 3: Call the Plugin from Unity
Create a C# script to interface with the plugin:
using UnityEngine; public class USBDataReceiver : MonoBehaviour { private AndroidJavaObject usbPlugin; void Start() { usbPlugin = new AndroidJavaObject("com.yourcompany.usbplugin.USBCommunication"); // Replace with your PC USB device's vendor and product IDs usbPlugin.Call("findAndRequestDevice", 0x1234, 0x5678); } // Called from the Android plugin when data arrives public void OnDataReceived(string data) { Debug.Log("Received from PC: " + data); // Process your data here } void OnDestroy() { usbPlugin?.Call("stopReading"); } }
2. ADB Reverse Proxy + Socket Communication
If you want to skip native Android code, this quick workaround uses ADB to forward a PC port to your Android device, then uses standard TCP sockets in Unity.
Step 1: Set Up Port Forwarding
On your PC, open a command prompt and run:
adb reverse tcp:8080 tcp:8080
This maps port 8080 on your PC to port 8080 on your Android device.
Step 2: PC Side - Socket Server
Write a simple Python server to send data:
import socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 8080)) server_socket.listen(1) print("Waiting for Android connection...") conn, addr = server_socket.accept() print("Connected to", addr) while True: data = input("Enter data to send: ") conn.send(data.encode('utf-8'))
Step 3: Unity Side - Socket Client
Add this C# script to a Unity GameObject:
using UnityEngine; using System.Net.Sockets; using System.Text; using System.Threading; public class SocketDataReceiver : MonoBehaviour { private TcpClient client; private NetworkStream stream; private Thread readThread; private bool isConnected = false; void Start() { readThread = new Thread(ConnectAndRead); readThread.Start(); } private void ConnectAndRead() { try { client = new TcpClient("localhost", 8080); isConnected = true; stream = client.GetStream(); byte[] buffer = new byte[1024]; int bytesRead; while (isConnected) { bytesRead = stream.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { string data = Encoding.UTF8.GetString(buffer, 0, bytesRead); Debug.Log("Received: " + data); // Process data here } } } catch (System.Exception e) { Debug.LogError("Socket error: " + e.Message); isConnected = false; } } void OnDestroy() { isConnected = false; stream?.Close(); client?.Close(); readThread?.Abort(); } }
Note: This requires USB debugging to be enabled on your Android device—great for prototyping, but not ideal for production.
3. Pre-built USB Serial Library for Android
Libraries like usb-serial-for-android handle low-level serial communication, so you don't have to write it from scratch.
Step 1: Import the Library
Download the library's AAR/JAR file and place it in Unity's Plugins/Android folder.
Step 2: Unity C# Wrapper
Here's a simplified wrapper to use the library:
using UnityEngine; using System.Threading; using System.Text; public class UsbSerialReceiver : MonoBehaviour { private AndroidJavaObject serialPort; private Thread readThread; private bool isReading = false; void Start() { AndroidJavaObject usbManager = new AndroidJavaClass("android.hardware.usb.UsbManager") .CallStatic<AndroidJavaObject>("getSystemService", UnityPlayer.currentActivity); AndroidJavaObject deviceList = usbManager.Call<AndroidJavaObject>("getDeviceList"); AndroidJavaObject entrySet = deviceList.Call<AndroidJavaObject>("entrySet"); AndroidJavaIterator iterator = entrySet.Call<AndroidJavaIterator>("iterator"); while (iterator.Call<bool>("hasNext")) { AndroidJavaObject entry = iterator.Call<AndroidJavaObject>("next"); AndroidJavaObject device = entry.Call<AndroidJavaObject>("getValue"); int vendorId = device.Call<int>("getVendorId"); int productId = device.Call<int>("getProductId"); if (vendorId == 0x1234 && productId == 0x5678) { AndroidJavaObject driver = new AndroidJavaObject("com.hoho.android.usbserial.driver.Cp21xxSerialDriver", device); serialPort = driver.Call<AndroidJavaObject>("getPorts")[0]; serialPort.Call("open", usbManager); serialPort.Call("setParameters", 9600, 8, 1, 0); StartReading(); break; } } } private void StartReading() { isReading = true; readThread = new Thread(() => { byte[] buffer = new byte[1024]; int bytesRead; AndroidJavaObject inputStream = serialPort.Call<AndroidJavaObject>("getInputStream"); while (isReading) { bytesRead = inputStream.Call<int>("read", buffer); if (bytesRead > 0) { string data = Encoding.UTF8.GetString(buffer, 0, bytesRead); Debug.Log("Received serial data: " + data); } } }); readThread.Start(); } void OnDestroy() { isReading = false; serialPort?.Call("close"); readThread?.Abort(); } }
Note: Don't forget to add the USB permissions to your AndroidManifest.xml as in the first solution.
Which Solution Should You Pick?
- Option 1: Best for production apps needing full USB control, no ADB dependency. Requires basic Android dev knowledge.
- Option 2: Fastest prototyping solution, but relies on USB debugging.
- Option 3: Balances ease of use and functionality for serial USB devices (like Arduino).
内容的提问来源于stack exchange,提问作者Kheoss - Vlad

