美文网首页
网络框架- Okhttp3与Volley整体架构分析

网络框架- Okhttp3与Volley整体架构分析

作者: Stan_Z | 来源:发表于2020-05-16 17:48 被阅读0次

一、Okhttp3

okhttp由square公司出品,目前主流的网络库,功能相对比较全面。

1.1总体框架设计

Okhttp 3.4之前有个类:HttpEngine,网络请求和响应的核心逻辑封装在HttpEngine中,3.4及其以后版本重构了,去掉了这个类,它的功能在getResponseWithInterceptorChain中拆分为CacheInterceptor、ConnectInterceptor、CallServerInterceptor 等几个拦截器来实现。

1.2请求流程
1.3 类图
1.4 异步请求时序图

基本流程:
1)Okhttp通过newCall创建RealCall发起一个网络请求任务,同时传入Request。
2)网络请求的调度交给Dispatcher通过一个受限制的Cached类型的线程池来处理。
3)3.4之前通过HttpEngine来处理网络请求,之后通过对应的几个固定拦截器来处理网络请求。
4)callback返回Response。

二、Volley

Volley 是 Google 1/0 2013 发布的Android异步网络请求框架和图片加载框架。适合数据量小,通信频繁的网络操作。

2.1总体框架设计

上层接受自定义Reuqest,请求放入RequestQueue中,通过两种Dispatch Thread不断从RequestQueue中取出请求,根据是否已缓存调用Cache或Network这两类数据获取接口之一,
从内存缓存或是服务器取得请求的数据,然后交由ResponseDelivery去做结果分发及回调处理。

2.2请求流程
2.3 类图
2.4 请求时序图

基本流程:
1)Volley创建一个RequestQueue,该Queue包含1个CacheDispatcher线程,4个NetworkDispatcher线程。
2)request设置能缓存则请求放入mCacheQueue,交由CacheDispatcher处理,获取缓存。不能缓存放入mNetworkQueue,走网络请求。
3)真正网络请求由BasicNetwork.performRequest执行。
4)最终解析response并返回callback。

三、Okhttp3 和 Volley几个点的对比

3.1 框架设计

这里只是对框架设计本身做对比,而非功能层面。

Okhttp3

优点

  • 首先设计上一个最大的直观感受是无处不在的建造者模式。不仅包括OkhttpClient、Request、Response这种核心组件,也包括CacheControl、Headers这种具体功能都使用的建造者模式,配置非常灵活。
  • 通过各种Interceptor以责任链模式对Request和Response进行监听、参数获取或者调整某些参数信息。

缺点

  • Request个性配置支持不够,例如:为不同Request设置不同的超时时间,因为超时是由OkHttpClient统一设置的,当然也可以对个性化定制的Request去走OkHttpClient.newBuilder(),但是这里考虑到并发问题,并不是一个非常好的策略。
  • Request中的配置信息基本上异常都是throw一个Exception,这也就要求初始化Request的时候必须做好try catch,不然线上环境很容易crash,例如:.url()设置请求url,如果url格式出现问题,会在HttpUrl.parse里直接throw异常,交给调用处去处理,这时候如果调用处疏忽没做处理,就会crash。
  • callBack返回的异常情况需要自行封装,严格说也不能叫做是缺点吧,只是这点Volley做得更好。
Volley

优点

  • Request拓展性非常强。
  • callBack异常有封装VolleyError,同时也封装了各种异常子类,这部分handleError功能写起来比Okhttp3方便很多。


缺点
BasicNetwork 中的statuCode状态码处理有点笼统,if (statusCode < 200 || statusCode > 299) 直接throw new IOException(); 另外还有volley 401 错误处理

3.2 重试
Okhttp3

通过拦截器RetryAndFollowUpInterceptor来做,因为拦截器是基于OkHttpClient来做的,如果是通用型重试还好,如果Request需要不同个性化重试策略,比如重试次数有差别,或者是否提供其他重试域名等等。这种情况下RetryAndFollowUpInterceptor就不太好做了,况且他还是final的,默认策略不满足还只能自己重新写。

Volley

Request.setRetryPolicy来实现,这对单一Request个性化定制来说比Okhttp3灵活,它接口为RetryPolicy,有个默认实现DefaultRetryPolicy,支持参数的动态配置,当然也可以自定义。相对比较灵活。

3.3 调度
Okhttp3

Dispatcher提供了在最大并发数64和每个主机最大请求数5的范围内的一个类似CacheThreadPool的线程池。这种类型线程池比较适用于:高并发但每个任务耗时较少的场景。

//准备队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//执行队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //最大并发数。
  private int maxRequests = 64;
  //每个主机的最大请求数。
  private int maxRequestsPerHost = 5;

executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));

那么来看看调度逻辑:

  void enqueue(AsyncCall call) {
    synchronized (this) {
      //加入双端准备队列
      readyAsyncCalls.add(call);
    }
    promoteAndExecute();
  }

将符合条件的calls从readyAsyncCalls移动到runningAsyncCalls里面,在executor service上执行它们。

  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      //循环判断准备请求队列是否还有请求
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
        //如果执行请求的队列数量大于等于最大请求数,中止循环
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        //小于最大主机请求限制
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
        //从准备队列里移除自身。
        i.remove();
        executableCalls.add(asyncCall);
        //添加到执行队列中。
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }
    //对新添加的任务尝试进行异步调用。
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }
    return isRunning;
  }

对应的AsyncCall 就是一个Runnable。

这里可以在源码的基础上做两个层面的自定义调度优先级:

1)线程层面的

可以通过自定义Request传入priority,在RealCall中获取对应Request的优先级并设置给AsyncCall

  final class AsyncCall extends NamedRunnable {
    ...
        @Override
        public void run() {
            super.run();
            //originalRequest 就是传进来的Request
            Thread.currentThread().setPriority(originalRequest.priority());
        }
2)线程池Queue层面的
     executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
                new PriorityBlockingQueue<Runnable>(60,new AsycCallComparator<Runnable>()), Util.threadFactory( "OkHttp Dispatcher", false));

这里使用PriorityBlockingQueue,它是一个具有优先级的无界队列,同时需要实现一个有序的比较器:

public class AsycCallComparator<T> implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        if ((o1 instanceof RealCall.AsyncCall) && (o2 instanceof RealCall.AsyncCall)) {
            RealCall.AsyncCall task1 = (RealCall.AsyncCall) o1;
            RealCall.AsyncCall task2 = (RealCall.AsyncCall) o2;
            int result = task2.priority() - task1.priority();
            return result;
        }
        return 0;
    }
}
Volley

Volley是1个Cache Thread,4个Network Thread。各自都有一个对应的PriorityBlockingQueue维护请求队列。

    /**
     * The cache triage queue.
     */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
            new PriorityBlockingQueue<>();

    /**
     * The queue of requests that are actually going out to the network.
     */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
            new PriorityBlockingQueue<>();
  /**
     * Number of network request dispatcher threads to start.
     */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        //mDispatchers.length 即为DEFAULT_NETWORK_THREAD_POOL_SIZE
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

对于优先级调度来说:
Volley本身mCacheQueue 和 mNetworkQueue 就使用的PriorityBlockingQueue,而线程优先级就更方便了,重构一个RequestQueue的start方法,直接给NetworkDispatcher设置优先级。

 public void start(int threadPriority) {
        stop(); // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
            try {
                networkDispatcher.setPriority(threadPriority);
            } catch (Exception e) {
                e.printStackTrace();
            }
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

最近完成了公司网络框架组件的重构,新增了Okhttp支持,在保证不动业务层调用的情况下,调整了网络库整体架构,支持Okhttp和Volley动态切换,并且从其他Module的耦合中剥离出来,单独做maven管理。从两个框架的使用情况来看,常规请求都是50-100ms量级的速度,两者不相上下。两者对get 、post 、multipart post支持都还不错。后续再进一步看看表现差异以及存在的问题。

相关文章

网友评论

      本文标题:网络框架- Okhttp3与Volley整体架构分析

      本文链接:https://www.haomeiwen.com/subject/wmylohtx.html