Volley的框架解读三(字节流的优化)

来源:互联网 发布:电视tv软件 编辑:程序博客网 时间:2024/06/04 00:54

先看UML。其实用到这两个东西就是在访问网络的时候

bytecauche

 

 

 

源码解读绝招二:你还记得UML,和设计模式二人组么,用程序员共用的语言去看他们吧~

在对响应的实体进行操作的时候,使用到了byte[] ,由于volley是轻量级频次高的网络请求框架,因此会大量使用到byte[] ,这样的话会频繁创建和销毁byte[]。为了提高性能,volley定义了一个byte[]缓冲池,即ByteArrayPool 。

在ByteArrayPool 内,定义了 两个集合,分别是存储按大小顺序排列byte[]的list 和 按使用先后顺序排列byte[]的LinkedList。在volley中所需要使用到的byte[]从该缓冲池中来取,当byte[]使用完毕后再归还到该缓冲池,从而避免频繁的创建和销毁byte[]。

如下:

    /**     *  按使用的先后时间顺序排序     */    private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();    /**     *  按大小顺序排序     */    private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);    /**     *  池中所有byte[]的长度之和     */    private int mCurrentSize = 0;    /**     * 池中单个byte[]的最大长度     */    private final int mSizeLimit;

以下内容可能跟数据结构相关了,比如数据结构,取,存,删除,容量自动扩增等等

数据取出

简而言之就是如果从缓存池中找不到长度比他大的,就新建一个,有的话就直接返回Byte[]

    public synchronized byte[] getBuf(int len) {        //遍历按长度排序的池        for (int i = 0; i < mBuffersBySize.size(); i++) {            byte[] buf = mBuffersBySize.get(i);            //如果当前的byte[]的长度大于给定的长度,就返回该byte[]            if (buf.length >= len) {                //池中所有byte[]的长度之和减去返回出去的byte[]的长度                mCurrentSize -= buf.length;                //从按顺序排列的池中移除该byte[]                mBuffersBySize.remove(i);                //从按使用顺序排列的池中移除该byte[],表示该byte[]正在使用中,其他不能再使用该byte[]                mBuffersByLastUse.remove(buf);                return buf;            }        }        //创建一个新的byte[]        return new byte[len];    }

数据存入

这里就是用完又要把byte[]放回来了。
数据结构一般不能忘记边界考虑了,比如为空,或者超过限制byte[]最大长度就不存入了,同时也要存入缓存池

    public synchronized void returnBuf(byte[] buf) {        //如果为空或者超过了设定的单个byte[]的最大长度  那么就不再池中保存该byte[]        if (buf == null || buf.length > mSizeLimit) {            return;        }        //在按使用时间顺序排序的池中添加该byte[]        mBuffersByLastUse.add(buf);        //利用二分查找法,找出在按大小排序的池中该byte[]应该存放的位置        int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);        //如果找不到,则返回-1        if (pos < 0) {            //当找不到时,也就是在0位置添加            pos = -pos - 1;        }        mBuffersBySize.add(pos, buf);        //增加最大长度        mCurrentSize += buf.length;        //清理,不能超过字节总数最大值        trim();    }

数据删除

上面有有个trim,也即是删除数据方法

    private synchronized void trim() {        //当现有字节总数超过了设定的界限,那么需要清理        while (mCurrentSize > mSizeLimit) {            //按照使用的先后顺序来倾全力,最新使用的最先被清除            byte[] buf = mBuffersByLastUse.remove(0);            //同样在该池中也清除            mBuffersBySize.remove(buf);            //减小现有字节最大长度            mCurrentSize -= buf.length;        }    }

写到这里的时候,还差一个自动扩增方法怎么没有呢,Volley是把它和ByteArrayOutputStream联合在一起了。

你没猜错又是组合,ByteArrayOutputStream的写入方法中,有自动扩存

   /**     * 从buffer的offset位置读len长度的内容,写入到输出流的byte[](buf)中     */    @Override    public synchronized void write(byte[] buffer, int offset, int len) {        expand(len);        super.write(buffer, offset, len);    }

expand方法~

 /**     * Ensures there is enough space in the buffer for the given number of additional bytes.     * 扩充当前输出流正在使用的byte[]的大小     */    private void expand(int i) {        /* Can the buffer handle @i more bytes, if not expand it */        if (count + i <= buf.length) {            //当已经写入的字节数加上预计扩展的字节数之和,仍然不大于当前的byte[]的长度时,不需要扩展            return;        }        //当当前的byte[]不再满足时,需要从byte[]缓冲池中获取一个byte[],大小为(count + i) * 2        byte[] newbuf = mPool.getBuf((count + i) * 2);        //将当前的byte[]内容复制到新的byte[]中        System.arraycopy(buf, 0, newbuf, 0, count);        //将旧的byte[]进行回收到byte[]缓冲池中        mPool.returnBuf(buf);        buf = newbuf;    }

当然也把存取也封装进了ByteArrayOutputStream

    public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {        mPool = pool;        /**         * buf,该输出流将要内容写入的目标byte[],如果多次写入,buf长度不够的时候需要扩展长度         */        buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));    }    @Override    public void close() throws IOException {        //当输出流关闭的时候,释放该byte[]回到byte缓冲池中        mPool.returnBuf(buf);        buf = null;        super.close();    }    @Override    public void finalize() {        //当垃圾回收机制准备回收该输出流时,将该byte[]回收到缓冲池        mPool.returnBuf(buf);    }

构造方法中,取出,close的时候返回,简直完美,有不有

这些缓存池用在那个地方呢? 也就是上一节用来缓存响应数据的

   private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {        PoolingByteArrayOutputStream bytes =                new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());        byte[] buffer = null;        try {            InputStream in = entity.getContent();            if (in == null) {                throw new ServerError();            }            buffer = mPool.getBuf(1024);            int count;            while ((count = in.read(buffer)) != -1) {                bytes.write(buffer, 0, count);//从buffer中读取内容写入输出流中的byte[]中            }            return bytes.toByteArray();        } finally {            try {                // Close the InputStream and release the resources by "consuming the content".                entity.consumeContent();            } catch (IOException e) {                // This can happen if there was an exception above that left the entity in                // an invalid state.                VolleyLog.v("Error occured when calling consumingContent");            }            mPool.returnBuf(buffer);            bytes.close();        }    }