安卓设备和PC之间有很多种通信方式,比如基于无线网络等等。有的时候我们可能需要一些其他的通信方式。安卓设备和PC之间可以通过使用USB来进行通信。使用USB通信能够比无线通信速度更快,效率更高,这一点在分布式中的研究中很有用处,比如我们使用多台手机做实验,记录下log,这几个log的时间最好是一样的,那么我们可以每次记录下log的时候,向PC获取一次时间,把PC的时间作为标准时间。以下给出基于USB通信的原理和实现方式。
安卓设备能够和PC之间使用Socket通信的关键一点是能够使用adb转发,所以手机要有adb模式。adb对端口进行映射,转发Socket中的数据,在Socket通信结构中,PC端只能作为Client端,安卓设备是作为Server端。
先看安卓端的实现,安卓端的步骤是建立服务端的监听过程,在连接上之后做各种处理。代码如下:
ServerSocket serverSocket = new ServerSocket(ADBExecutor.ANDROID_PORT);
Socket socket = serverSocket.accept();
SomeService.INSTANCE.setIOStream(socket);
这里ADBExecutor.ANDROID_PORT是自己定义的一个端口,这个用户自己随意定义。对于SomeService类也在这里给出代码吧:
public enum SomeService {
INSTANCE;
private DataInputStream inputStream = null;
private DataOutputStream outputStream = null;
public void setIOStream(final Socket socket){
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void pollingTime(){
try {
outputStream.writeUTF("REQ");
outputStream.flush();
long time = inputStream.readLong();
System.out.println("PC time:" + time + " \n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
可以在这中间做想要的事情。比如我定义了一个方法为pollingTime(),就是获取PC的时间并打印。
接下来看PC端的代码的操作。在PC端需要首先确定有哪些设备通过USB连接上的,这个可以得到当前连接的设备的机器序列号。然后通过执行如下命令:
adb -s device_id forward tcp:host_port tcp:to_port
来完成端口的转发映射。其中device_id就是对应的机器的硬件地址。host_port是PC定义的一个端口,to_port是指定的转发到的端口。而关于device_id怎么得到呢,执行如下命令即可:
adb devices
关于怎么在java中执行命令,以及收集命令执行的结果,建立端口的映射,建立Socket连接,给出如下代码:
public class ADBExecutor {
public static final int ANDROID_PORT = 30000;
public static final int HOST_BASE_PORT = 35000;
private final String adb_directory;
public ADBExecutor(String adb_directory){
this.adb_directory = adb_directory;
}
//得到当前连接上的设备的序列号
public List execAdbDevices()
{
System.out.println("adb devices");
List ret_device_id_list = new ArrayList();
Process proc = null;
try
{
proc = new ProcessBuilder(this.adb_directory, "devices").start();
proc.waitFor();
} catch (IOException ioe)
{
ioe.printStackTrace();
} catch (InterruptedException ire)
{
ire.printStackTrace();
}
String devices_result = this.collectResultFromProcess(proc);
String[] device_id_list = devices_result.split("\\r?\\n");
if (device_id_list.length <= 1)
{
System.out.println("No Devices Attached.");
return ret_device_id_list;
}
/**
* collect the online devices
*/
String str_device_id = null;
String device = null;
String[] str_device_id_parts = null;
// ignore the first line which is "List of devices attached"
for (int i = 1; i < device_id_list.length; i++)
{
str_device_id = device_id_list[i];
str_device_id_parts = str_device_id.split("\\s+");
// add the online device
if (str_device_id_parts[1].equals("device"))
{
device = str_device_id_parts[0];
ret_device_id_list.add(device);
System.out.println(device);
}
}
//System.exit(0);
return ret_device_id_list;
}
//对于当前连接上的设备都进行映射
public Map execAdbOnlineDevicesPortForward()
{
List device_id_list = this.execAdbDevices();
Map device_hostport_map = new HashMap();
int index = 0;
for (String device : device_id_list)
{
int host_port = ADBExecutor.HOST_BASE_PORT + index * 10;
this.execAdbSingleDevicePortForward(device, host_port, ADBExecutor.ANDROID_PORT);
device_hostport_map.put(device, host_port);
index++;
}
return device_hostport_map;
}
//具体的映射过程
public void execAdbSingleDevicePortForward(String device_id, int host_port, int to_port)
{
System.out.println("adb -s " + device_id + " forward tcp:" + host_port + " tcp:" + to_port);
Process proc = null;
try
{
proc = new ProcessBuilder(this.adb_directory, "-s", device_id, "forward", "tcp:" + host_port, "tcp:" + to_port).start();
proc.waitFor();
} catch (IOException ioe)
{
ioe.printStackTrace();
} catch (InterruptedException ire)
{
ire.printStackTrace();
}
}
//手机命令执行之后的结果
private String collectResultFromProcess(Process proc)
{
StringBuilder sb_result = new StringBuilder();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String result_line = null;
try
{
while ((result_line = stdInput.readLine()) != null)
{
sb_result.append(result_line);
sb_result.append("\n");
}
while ((result_line = stdError.readLine()) != null)
{
sb_result.append(result_line);
sb_result.append("\n");
}
} catch(IOException ioe)
{
ioe.printStackTrace();
}
return sb_result.toString();
}
}
以上是ADB执行各种操作的类,比如查看当前连接的设备,建立端口映射。接下来给出PC上执行的主要过程:
package com.example.adbforward;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
public class PCHost {
private final Map device_hostport_map;
private final Map device_hostsocket_map = new HashMap();
public PCHost(Map device_hostport_map){
this.device_hostport_map = device_hostport_map;
this.createDeviceHostConnection();
}
//创建连接
private void createDeviceHostConnection()
{
String device = null;
int host_port = -1;
for(Map.Entry device_hostport : this.device_hostport_map.entrySet())
{
device = device_hostport.getKey();
host_port = device_hostport.getValue();
Socket device_hostsocket = null;
try
{
device_hostsocket = new Socket("localhost", host_port);
} catch (UnknownHostException uhe)
{
uhe.printStackTrace();
} catch (IOException ioe)
{
ioe.printStackTrace();
}
this.device_hostsocket_map.put(device, device_hostsocket);
}
}
public void startTimeService(){
//启动某些服务线程
}
public static void main(String[] args) throws InterruptedException
{
ADBExecutor adb_executor = new ADBExecutor("E:\\eclipse\\sdk\\platform-tools\\adb.exe ");//这是我的abd.exe的位置
Map device_hostport_map = adb_executor.execAdbOnlineDevicesPortForward();
final PCHost host = new PCHost(device_hostport_map);
host.startTimeService();
}
}
以上就是整个android和PC之间通过USB采用Socket通信的主要过程。给出的代码能够对连接在PC上的多个设备都开启服务,即如果有多个设备连接在PC上。以上来自师兄的指点,在此感谢。