VolleySupport使用说明

1、概述

VolleySupport是基于Google的Volley框架,在其基础上简单封装完成的。添加的代码主要位于manager目录下。Volley的源码已经加入到Android Source中,路径为frameworks/volley

2 Volley源码分析

2.1 功能说明

volley提供功能如下:

JSON,图像等的异步下载;
网络请求的排序(scheduling)
网络请求的优先级处理
缓存
多级别取消请求
和Activity和生命周期的联动(Activity结束时同时取消所有网络请求)

2.2 Volley整体设计思路

首先添加实现Request抽象类的任务到RequestQueue中
然后通过Dispatch不断从RequestQueue中取出请求:
    优先判断CacheDispatcher中是否有缓存数据
    Cache中没有则通过NatworkDispatcher,从网络获取数据并缓存。
最后通过ResponseDelivery分发数据,做回调处理。

如下图所以:
这里写图片描述

2.3 主要类分析

Volley:Volley 工具类,对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。这里进行了版本号判断,大于等于9就使得HttpStack对象的实例为HurlStack,小于9则实例为HttpClientStack。

Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest等都是它的子类,表示某种类型的请求。

RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。

mCacheQueue 缓存请求队列
mNetworkQueue 网络请求队列
mCurrentRequests 正在进行中(正在请求中或者正在分发中),尚未完成的请求集合
mWaitingRequests 等待请求的集合,如果一个请求正在被处理并且可以被缓存,后续的相同 url 的请求,将进入此等待队列

CacheDispatcher:一个线程,用于调度处理缓存请求。启动后会不断从缓存请求队列mCacheQueue中取请求处理:

对于已经取消的请求,标记为跳过并结束这个请求;

新的或者过期的请求,直接放入mNetworkQueue中由N个NetworkDispatcher进行处理;

已获得缓存信息(网络应答)却没有过期的请求,由Request的parseNetworkResponse进行解析,从而确定此应答是否成功。如果需要更新缓存那么该请求还会被放入mNetworkQueue中由N个NetworkDispatcher进行处理。

最后将请求和应答交由Delivery(ExecutorDelivery)分发者进行处理,

NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。Volley中是使用了一个NetworkDispatcher线程数组去处理网络请求的。

ResponseDelivery:返回结果分发接口,最终调用Listener和ErrorListener方法通知UI线程。如果等待列表mWaitingRequests中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。目前实现类只有基于ExecutorDelivery的在入参 handler 对应线程内进行分发。

HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。

Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。其实现类为BasicNetwork。

Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。volley还提供了一个NoCache,也就是无缓存。

NetworkDispatcher得到请求结果后判断是否需要存储在 Cache。

CacheDispatcher会从 Cache 中取缓存结果。

类图如下:
这里写图片描述

2.4 添加请求

添加请求过程add分析如下
这里写图片描述

2.5 缓存处理

volley处理请求流程:
这里写图片描述

2.6 网络请求

这里写图片描述

2.7 Response转换

这里写图片描述

2.8 数据分发处理

这里写图片描述

3对volley的封装

主要位于manager包中。

3.1 RequestManager

简单封装了初始化和获取RequesQueue的方法。

public static void init(Context context, String userAgent) {
    RequestManager.init(context, userAgent, 4);
}

public static void init(Context context, String userAgent, int threadPool) {
    mRequestQueue = Volley.newRequestQueue(context, userAgent, threadPool);
}

3.2 Multipart请求支持

流格式上传文件支持包含三个文件

1、MultipartRequestParams为参数列表,支持两种类型参数

普通键值对
文件或文件流

MultipartRequestParams内部其实是一个FileWrapper,用来封装需要上传的流(文件最终也会被转换成流)。

private static class FileWrappers {
    public InputStream[] inputStreams;
    public String[] fileNames;
    public String contentType;

    public FileWrappers(InputStream[] inputStreams, String[] fileNames,
                        String contentType) {
        this.inputStreams = inputStreams;
        this.fileNames = fileNames;
        this.contentType = contentType;
    }

    public String getFileName(int i) {
        if (fileNames != null) {
            return fileNames[i];
        } else {
            return "nofilename";
        }
    }
}

MultipartRequestParams提供了两类put方法,一种是类似map的可以存储键值对,这里略过;另一种可以put进来流或者文件,如下。

public void put(String key, File[] files) {
    try {
        FileInputStream[] inputStreams = new FileInputStream[files.length];
        String[] fileNames = new String[files.length];
        for (int i = 0; i < files.length; i++) {
            if (files[i] != null) {
                inputStreams[i] = new FileInputStream(files[i]);
                fileNames[i] = files[i].getName();
            } else {
                inputStreams[i] = null;
                fileNames[i] = null;
            }
        }
        put(key, inputStreams, fileNames);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

public void put(String key, InputStream[] streams, String[] fileNames) {
    put(key, streams, fileNames, null);
}

public void put(String key, InputStream[] streams, String fileNames[], String contentType) {
    if (key != null && streams != null) {
        fileParams.put(key, new FileWrappers(streams, fileNames, contentType));
    }
}

最终文件保存在了fileParams中。

MultipartRequestParams还提供了一个方法getEntity,用来将前面添加进来的参数和流封装成HttpEntity。

public HttpEntity getEntity() {
    HttpEntity entity = null;
    MultipartEntity multipartEntity = new MultipartEntity();

    // Add string params
    for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
        multipartEntity.addPart(entry.getKey(), entry.getValue());
    }

    if (!fileParams.isEmpty()) {
        // Add file params
        int currentIndex = 0;
        int lastIndex = fileParams.entrySet().size() - 1;
        for (ConcurrentHashMap.Entry<String, FileWrappers> entry : fileParams.entrySet()) {
            FileWrappers file = entry.getValue();
            if (file.inputStreams != null) {
                boolean last = currentIndex == lastIndex;
                for (int i = 0; i < file.inputStreams.length; i++) {
                    boolean isLast;
                    if (i == file.inputStreams.length - 1) {
                        isLast = last & true;
                    } else {
                        isLast = last & false;
                    }
                    if (file.contentType != null) {
                        multipartEntity.addPart(entry.getKey(), file.getFileName(i), file.inputStreams[i], file.contentType, isLast);
                    } else {
                        multipartEntity.addPart(entry.getKey(), file.getFileName(i), file.inputStreams[i], isLast);
                    }
                }
            }
            currentIndex++;
        }
    }
    entity = multipartEntity;

    return entity;
} 

2.这里新建了一个MultipartEntity,提供了addPart方法添加流以及普通键值对,最终通过out写入流中。

public void addPart(final String key, final String fileName, final InputStream fin, String type, final boolean isLast) {
    writeFirstBoundaryIfNeeds();
    try {
        type = "Content-Type: " + type + "\r\n";
        out.write(("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + fileName + "\"\r\n").getBytes());
        out.write(type.getBytes());
        out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes());
        final byte[] tmp = new byte[4096];
        int l = 0;
        if (fin != null) {
            while ((l = fin.read(tmp)) != -1) {
                out.write(tmp, 0, l);
            }
        }
        if (!isLast) {
            out.write(("\r\n--" + boundary + "\r\n").getBytes());
        } else {
            writeLastBoundaryIfNeeds();
        }
        out.flush();
    } catch (final IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fin != null) {
                fin.close();
            }
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }
}

3.最终实现了一个MultipartRequest,这里继承的是StringRequest,因为差别不大。重写了getBody()方法,把MultipartEntity中的Entry读取出来,通过getBody返回一个字节数组给Http。(默认的getbody是获取Post的参数数据返回字节数组)

public byte[] getBody() throws AuthFailureError {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    if(params != null) {
        httpEntity = params.getEntity();
        try {
            httpEntity.writeTo(baos);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return baos.toByteArray();
}

4 取消请求

RequestQueue中添加了一个取消请求的方法,可以在不需要这个网络请求的时候通过tag取消它。

public void cancelAll(final Object tag) {
    if (tag == null) {
        throw new IllegalArgumentException("Cannot cancelAll with a null tag");
    }
    cancelAll(new RequestFilter() {
        @Override
        public boolean apply(Request<?> request) {
            return request.getTag() == tag;
        }
    });
}

5 重试机制

volley提供了重试机制接口RetryPolicy,且提供了一个默认的实现类DefaultRetryPolicy

public class DefaultRetryPolicy implements RetryPolicy {
    private int mCurrentTimeoutMs;  //当前超时时间毫秒值
    private int mCurrentRetryCount; //当前重试次数
    private final int mMaxNumRetries;   //最大重试次数
    private final float mBackoffMultiplier;//超时重试延时

    public static final int DEFAULT_TIMEOUT_MS = 15*1000;
    public static final int DEFAULT_MAX_RETRIES = 2;
    public static final float DEFAULT_BACKOFF_MULT = 1f;

    public DefaultRetryPolicy() {//使用默认的构造方法参数实例化
        this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
    }

    public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
        mCurrentTimeoutMs = initialTimeoutMs;
        mMaxNumRetries = maxNumRetries;
        mBackoffMultiplier = backoffMultiplier;
    }

    @Override
    public void retry(VolleyError error) throws VolleyError {
        mCurrentRetryCount++;
        //超时次数*重试延时 结果为下次重试网络请求时间,支持定制曲线增加时间。
        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
        if (!hasAttemptRemaining()) {
            throw error;
        }
    }

    protected boolean hasAttemptRemaining() {
        return mCurrentRetryCount <= mMaxNumRetries;
    }
}

6 地址

maven地址 com.android.common:VolleySupport:1.0.1

源码地址 maven目录下

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值