Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 856|回复: 0

开发交流:Android开发进阶之NIO非阻塞包(三)

[复制链接]

该用户从未签到

发表于 2011-10-24 10:50:41 | 显示全部楼层 |阅读模式
有关Android NIO的相关内容,本次整理并归类如下,为了让大家感觉NIO和Android平台联系的紧密,这里我们结合ADT插件的重要开发工具DDMS中的源码进行分析。在android git中的sdk.git文件中,可以找到ddmlib这个文件夹。有关PC和手机的互通内核在这里使用了java来完全实现。这里一起帮助大家了解下PC同步软件的开发原理同时学习下Java中的New I/O技术。   比较重要的代码段我们贴出,逐一分析,其他的网友可以直接预读源码:
   AdbHelper.java文件中 public static SocketChannel open(InetSocketAddress adbSockAddr,

            Device device, int devicePort) //这是一个重载版本,主要是关联Device实例。

            throws IOException, TimeoutException, AdbCommandRejectedException {

        SocketChannel adbChan = SocketChannel.open(adbSockAddr); //构造SocketChannel对象,使用常规的open方法创建

        try {

            adbChan.socket().setTcpNoDelay(true); //设置TCP非延迟

            adbChan.configureBlocking(false); //非阻塞

            setDevice(adbChan, device); //本句和NIO没有多大关系,这句是指定具体的设备,比如模拟器,或Android手机的厂家代号,比如宏达电的以HTXXXXX这样的方式

            byte[] req = createAdbForwardRequest(null, devicePort); //设置端口转发,这句很关键,否则PC和手机通过USB是无法互通的。

            write(adbChan, req); //发送数据

            AdbResponse resp = readAdbResponse(adbChan, false); //读取收到的内容

            if (resp.okay == false) {

                throw new AdbCommandRejectedException(resp.message);

            }

            adbChan.configureBlocking(true);

        } catch (TimeoutException e) { //一般要处理超时异常

            adbChan.close(); //释放channel句柄

            throw e;

        } catch (IOException e) { //处理常规的IO异常

            adbChan.close();

            throw e;

        }

        return adbChan;

    }
复制代码有关读取ADB返回的报文方法 static AdbResponse readAdbResponse(SocketChannel chan, boolean readDiagString)

            throws TimeoutException, IOException {

        AdbResponse resp = new AdbResponse();

        byte[] reply = new byte[4]; //创建4字节数组,主要检测成功与否,adb的协议是成功返回 okay,失败fail,等等。

        read(chan, reply); //读取具体的返回

        if (isOkay(reply)) { //判断是否成功

            resp.okay = true;

        } else {

            readDiagString = true; // look for a reason after the FAIL

            resp.okay = false;

        }

        // not a loop -- use "while" so we can use "break"

        try {

            while (readDiagString) {

                // length string is in next 4 bytes

                byte[] lenBuf = new byte[4];

                read(chan, lenBuf); //读取一个字节数组,最终为了转为一个整形

                String lenStr = replyToString(lenBuf); //字节数组转为String

                int len;

                try {

                    len = Integer.parseInt(lenStr, 16); //String转为整形,这里提示,这种写法可能比较愚蠢,但是下面为Log输出提供了一点点的便利。

                } catch (NumberFormatException nfe) {

                    Log.w("ddms", "Expected digits, got '" + lenStr + "': "

                            + lenBuf[0] + " " + lenBuf[1] + " " + lenBuf[2] + " "

                            + lenBuf[3]);

                    Log.w("ddms", "reply was " + replyToString(reply));

                    break;

                }

                byte[] msg = new byte[len];

                read(chan, msg);

                resp.message = replyToString(msg);

                Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='"

                        + resp.message + "'");

                break;

            }

        } catch (Exception e) {

            // ignore those, since it's just reading the diagnose string, the response will

            // contain okay==false anyway.

        }

        return resp;

    }

   有关PC上对Android手机屏幕截图的方法之一:

   static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device)

            throws TimeoutException, AdbCommandRejectedException, IOException {

        RawImage imageParams = new RawImage();

        byte[] request = formAdbRequest("framebuffer:"); // 读取手机端adbd服务器的framebuffer调用返回的数组

        byte[] nudge = {

            0

        };

        byte[] reply;

        SocketChannel adbChan = null;

        try {

            adbChan = SocketChannel.open(adbSockAddr);

            adbChan.configureBlocking(false); //非阻塞

            setDevice(adbChan, device); //设置我们关系的设备

            write(adbChan, request); //发送framebuffer这个请求了

            AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);

            if (resp.okay == false) {   //判断返回是否ok。

                throw new AdbCommandRejectedException(resp.message);

            }

            reply = new byte[4];

            read(adbChan, reply); //首先返回的是一个协议,目前分为两个版本,主要是兼容模式和标准的模式,兼容模式比较少见,在2.0以后几乎看不到了。部分早期的1.6或更老的T-Mobile G1会使用兼容模式,模式不同,输出的截图中的颜色编码方式略有不同。

            ByteBuffer buf = ByteBuffer.wrap(reply);

            buf.order(ByteOrder.LITTLE_ENDIAN); //小头字节顺序

            int version = buf.getInt(); //ByteBuffer直接转int的方法,比较方便不用自己从字节数组中构造,按位计算

            int headerSize = RawImage.getHeaderSize(version); //根据返回的adb截图协议版本判断将收到的字节大小

            reply = new byte[headerSize * 4]; //分配空间,具体大小需要看协议版本

            read(adbChan, reply);

            buf = ByteBuffer.wrap(reply); //从reply数组实例化ByteBuffer

            buf.order(ByteOrder.LITTLE_ENDIAN); //注意字节序列,毕竟远端的adbd是工作在linux系统的手机上。

            if (imageParams.readHeader(version, buf) == false) { //判断是否有效,兼容这种截图协议。

                Log.e("Screenshot", "Unsupported protocol: " + version);

                return null;

            }

            Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="

                    + imageParams.size + ", width=" + imageParams.width

                    + ", height=" + imageParams.height); //打印下截图的基本信息,比如bpp代表色深,size是需要分配dib图像的字节数组。比较原始,

            write(adbChan, nudge); //发送一个字节,代表准备接收字节数组了

            reply = new byte[imageParams.size]; //分配和图像大小一样的字节数组

            read(adbChan, reply); //接收图像字节数组,这里Android开发网提示大家对于Android 1.x可能为RGB565,分配大小为 wxhx2xsize ,而2.x以后基本上为32位的RGB8888,分配大小为wxhx4xsize

            imageParams.data = reply;

        } finally {

            if (adbChan != null) {

                adbChan.close();

            }

        }

        return imageParams;

    }
复制代码继续就Android DDMS源码一起分析NIO非阻塞通讯方式,给大家分享下手机和PC互通中的一些技术。在NIO中有关SocketChannel和ByteBuffer的使用细节,可以在今天文章中   static void read(SocketChannel chan, byte[] data, int length, int timeout)

            throws TimeoutException, IOException {

        ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length); //从字节数组中实例化ByteBuffer

        int numWaits = 0;

        while (buf.position() != buf.limit()) {  //循环接收数据

            int count;

            count = chan.read(buf);

            if (count < 0) {

                    throw new IOException("EOF"); //读到末尾

            } else if (count == 0) {

                if (timeout != 0 && numWaits * WAIT_TIME > timeout) {

                    throw new TimeoutException();

                }

                 try {

                    Thread.sleep(WAIT_TIME);

                } catch (InterruptedException ie) {

                }

                numWaits++;

            } else {

                numWaits = 0;

            }

        }

    }
复制代码有关SocketChannel的写操作,就是发送数据代码如下: static void write(SocketChannel chan, byte[] data, int length, int timeout)

            throws TimeoutException, IOException {

        ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length);

        int numWaits = 0;

        while (buf.position() != buf.limit()) {

            int count;

            count = chan.write(buf); //发送数据从ByteBuffer中

            if (count < 0) {

                       throw new IOException("channel EOF");

            } else if (count == 0) {

                             if (timeout != 0 && numWaits * WAIT_TIME > timeout) {

                            throw new TimeoutException();

                }

                 try {

                    Thread.sleep(WAIT_TIME);

                } catch (InterruptedException ie) {

                }

                numWaits++;

            } else {

                numWaits = 0;

            }

        }

    }
复制代码有关ADB如何选择一个具体的设备,可以使用 setDevice 方法,这样当电脑中有模拟器或链接了多个手机,可以通过设备序列号,选择需要通讯的设备。   static void setDevice(SocketChannel adbChan, IDevice device)

            throws TimeoutException, AdbCommandRejectedException, IOException {

        // if the device is not -1, then we first tell adb we're looking to talk

        // to a specific device

        if (device != null) {

            String msg = "host:transport:" + device.getSerialNumber(); // 最后的获取序列号,提示大家在adb命令中是adb get-serialno

            byte[] device_query = formAdbRequest(msg);

            write(adbChan, device_query);

            AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);

            if (resp.okay == false) {

                throw new AdbCommandRejectedException(resp.message,

                        true/*errorDuringDeviceSelection*/);

            }

        }

    }

    通过PC控制手机重启的代码,当然这里需要Root权限才能执行

    public static void reboot(String into, InetSocketAddress adbSockAddr,

            Device device) throws TimeoutException, AdbCommandRejectedException, IOException {

        byte[] request;

        if (into == null) {

            request = formAdbRequest("reboot:"); //$NON-NLS-1$

        } else {

            request = formAdbRequest("reboot:" + into); //$NON-NLS-1$

        }

        SocketChannel adbChan = null;

        try {

            adbChan = SocketChannel.open(adbSockAddr);

            adbChan.configureBlocking(false);

            // if the device is not -1, then we first tell adb we're looking to talk

            // to a specific device

            setDevice(adbChan, device);

            write(adbChan, request);

        } finally {

            if (adbChan != null) {

                adbChan.close();

            }

        }

    }
复制代码



Android开发进阶之NIO非阻塞包(一)Android开发进阶之NIO非阻塞包(二)
Android开发进阶之NIO非阻塞包(四)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2025-1-11 07:51 , Processed in 0.381182 second(s), 46 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表