前几天研究截屏,查看ddmlib源码,发现adb通信是通过socket完成的,通过流传输可以提高效率。
adb 命令:adb forward,可以创建socket,官方解释如下
Forwarding Ports
You can use the forward
command to set up arbitrary port forwarding — forwarding of requests ona specific host port to a different port on an emulator/deviceinstance. Here's how you would set up forwarding of host port 6100 toemulator/device port 7100:
adb forward tcp:6100 tcp:7100
我的理解就是pc机端口127.0.0.1:6100映射到Android设备的7100端口。
模型图
我以PC为客户端,Android设备为服务端写个例子。
PC端口为1352,Android设备端口为2235,这是我的电话号码前几位。
Android服务端:
写一个监听2235端口的服务
首先添加权限
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Java代码
服务端监听连接,连接后发一个ack "you connect me"给pc客户端,等待用户发过来的消息。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); try { mServerSocket = new ServerSocket(2235); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } protected void onDestroy() { bRun = false; } public void onClick(View vt) { Log.i(TAG, "start listen socket accept"); acceptThread(); } private void acceptThread(){ if (bRun) return; bRun = true; Runnable r = new Runnable() { @Override public void run() { // TODO Auto-generated method stub while(bRun) { Socket sock = accept(); if (sock != null) { Log.i(TAG, "sock is not null ptr:" + sock.getPort() + " is connect"); new SocketChild(sock).start(); } else { //Log.i(TAG, "sock is null"); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; new Thread(r).start(); } private Socket accept() { Socket sock = null; try { sock = mServerSocket.accept(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return sock; } public class SocketChild extends Thread{ private Socket mSocket; public SocketChild(Socket sock) { // TODO Auto-generated constructor stub mSocket = sock; ack("you connect me"); } private void ack(String ack) { try { mSocket.getOutputStream().write(ack.getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { while(bRun) { try { sleep(100); read(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { mSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void read() throws IOException { InputStream input; byte [] buffer = new byte[1024]; input = mSocket.getInputStream(); int len = input.read(buffer, 0, 1024); Log.i(TAG, new String(buffer).substring(0, len)); } }
PC端
准备条件:
1、配置好adb环境
2、使用TCP调试工具
建立连接,在PC端CMD输入:adb forward tcp:1352 tcp:2235
代开TCP调试工具,连接1352端口,发送"hello world",得到Android设备回传的"you connect me".
再看Android服务端
我这边偷懒,直接用Locat打印Android服务端显示
这就完成了一个完整的连接,发送,应答的过程。
通过这个命令可以引申很多东西,如果你想使用screencap -p持续生成图像,再使用adb pull导出来,一次过程也得几秒,使用这种流的方式就高效多了,可以参考/system/core/adb/framebuffer_service.c的方式把图像数据导出来,这样就可以保证图像的流畅度。framebuffer_service.c 执行screencap获取原始的图像数据,再写到socket。
以下是framebuffer_service.c的参考代码:
void framebuffer_service(int fd, void *cookie) { struct fbinfo fbinfo; unsigned int i; char buf[640]; int fd_screencap; int w, h, f; int fds[2]; if (pipe(fds) < 0) goto done; pid_t pid = fork(); if (pid < 0) goto done; if (pid == 0) { dup2(fds[1], STDOUT_FILENO); close(fds[0]); close(fds[1]); const char* command = "screencap"; const char *args[2] = {command, NULL}; execvp(command, (char**)args); exit(1); } fd_screencap = fds[0]; /* read w, h & format */ if(readx(fd_screencap, &w, 4)) goto done; if(readx(fd_screencap, &h, 4)) goto done; if(readx(fd_screencap, &f, 4)) goto done; fbinfo.version = DDMS_RAWIMAGE_VERSION; /* see hardware/hardware.h */ switch (f) { case 1: /* RGBA_8888 */ fbinfo.bpp = 32; fbinfo.size = w * h * 4; fbinfo.width = w; fbinfo.height = h; fbinfo.red_offset = 0; fbinfo.red_length = 8; fbinfo.green_offset = 8; fbinfo.green_length = 8; fbinfo.blue_offset = 16; fbinfo.blue_length = 8; fbinfo.alpha_offset = 24; fbinfo.alpha_length = 8; break; case 2: /* RGBX_8888 */ ...... default: goto done; } /* write header */ if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done; /* write data */ for(i = 0; i < fbinfo.size; i += sizeof(buf)) { if(readx(fd_screencap, buf, sizeof(buf))) goto done; if(writex(fd, buf, sizeof(buf))) goto done; } if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done; if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done; done: TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); close(fds[0]); close(fds[1]); close(fd); }
有耐心的朋友可以看看adb源码、ddmlib源码
adb源码:android源码内/system/core/adb
ddmlib源码:http://www.boyunjian.com/javasrc/com.google.android.tools/ddmlib/r13/_/com/android/ddmlib/