前几天研究截屏,查看ddmlib源码,发现adb通信是通过socket完成的,通过流传输可以提高效率。
adb 命令:adb forward,可以创建socket,官方解释如下
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端口的服务
首先添加权限
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/