Volley+OkHttp学习笔记


OkHttp支持 Http1.0/1.1/2/0、SPDY协议,用来进行客户端和服务器的连接以及数据交互,扮演着传输层的角色。Volley负责处理请求,加载,缓存,线程,同步等问题。先来简单说下Volley的上层部分。用Volley进行网络请求的调用方式如下:

RequestQueue mQueue = Volley.newRequestQueue(Context);
mQueue.start();
StringRequest stringRequest = new StringRequest(...);
mQueue.add(stringRequest);
//添加请求

下面来看一下Volley的部分源码

public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 
  File cacheDir = new File(context.getCacheDir(), "volley"); 
  String userAgent = "volley/0"; 
  try { 
    String network = context.getPackageName(); 
    PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
    userAgent = network + "/" + queue.versionCode; 
  } catch (NameNotFoundException var6) { 
    ; 
  }
  if(stack == null) { 
    if(VERSION.SDK_INT >= 9) { 
      stack = new HurlStack(); 
    } else { 
      stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); 
    } 
  } 
  //由此可见Volley支持自定义网络请求,继承HttpStack即可。Volley+OkHttp整合就是这样的 
  BasicNetwork network1 = new BasicNetwork((HttpStack)stack);  
  RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1); 
  queue1.start(); 
  return queue1;
}

所有的网络请求都是通过添加到请求队列RequestQueue来执行的,那么RequestQueue执行网络请求的原理又是什么呢?

//线程安全的加减操作
private AtomicInteger mSequenceGenerator;
//等候缓存队列 key->url 如果我们有多个请求的url都是相同的,也就是说请求的资源是相同的,volley就把这些请求放入一个队列,
//在用url做key将队列放入map中。
private final Map<String, Queue<Request<?>>> mWaitingRequests;
//所有在队列中,或者正在被处理的请求都会在这个集合中//缓存队列与网络队列的总和
private final Set<Request<?>> mCurrentRequests;
//缓存请求队列 有阻塞功能//阻塞功能的本质是读取队列中元素调用take()方法中:读写锁,若返回请求为空阻塞线程,直到
//request不为空 返回request,先进先出 PriorityBlockingQueue添加了Lock锁
private final PriorityBlockingQueue<Request<?>> mCacheQueue;
//请求为从网络中直接获取的队列 有阻塞功能
private final PriorityBlockingQueue<Request<?>> mNetworkQueue;
//默认用于网络调度的线程池数目
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
//缓存
private final Cache mCache;
//执行请求的网络
private final Network mNetwork;
//派发请求结果的接口,通过Handler向主线程发送消息private final ResponseDelivery mDelivery;
//该队列的所有网络调度器 多个线程 全部开启 extends Thread
private NetworkDispatcher[] mDispatchers;
//该队列的所有缓存调度器 线程 开启后一直循环调度 extends Thread
private CacheDispatcher mCacheDispatcher;

上边是RequestQueue中所有的全局变量,由这些变量能看出什么吗?RequestQueue添加网络请求可以执行的本质其实是:创建RequestQueue实例,调用start()方法后,a.创建了一个缓存调度器(也就是线程),用来执行已执行且可缓存的网络请求; b.创建了多个网络调度器(也就是线程),用来执行未执行、或者缓存已过期等需要请求服务器的网络请求

public void start() { 
  this.stop(); 
  // 保证所有正在运行的Dispatcher(也就是线程)都停止 
  // 创建缓存的派发器(也是一个线程),并启动线程。       
  this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery); 
  this.mCacheDispatcher.start(); 
  for(int i = 0; i < this.mDispatchers.length; ++i) { 
    NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
    this.mDispatchers[i] = networkDispatcher; 
    networkDispatcher.start(); 
  }
}

下边来看下NetworkDispatcher,让我们开看看网络调度器是怎么具体执行网络请求的。一般情况下,使用Volley框架,我们会在应用初始化的时候实例RequestQueue并启动,同时启动多个网络调度线程。

public void run() { 
  Process.setThreadPriority(10); 
  while(true) { 
    long startTimeMs; 
    Request request; 
    while(true) { 
      startTimeMs = SystemClock.elapsedRealtime(); 
      try { //请求队列具有阻塞功能 
        request = (Request)this.mQueue.take(); 
        break; 
      } catch (InterruptedException var6) { 
        if(this.mQuit) { 
          return; 
        } 
      } 
    } try { 
      request.addMarker("network-queue-take"); 
      if(request.isCanceled()) { 
        request.finish("network-discard-cancelled"); 
      } else { 
        this.addTrafficStatsTag(request); 
        NetworkResponse e = this.mNetwork.performRequest(request); //最终的结果获得 
        BasicNetwork.performRequest(); 
        request.addMarker("network-http-complete"); 
        if(e.notModified && request.hasHadResponseDelivered()) { 
          //已有请求结果,并且有效 
          request.finish("not-modified"); 
        } else { 
          Response volleyError1 = request.parseNetworkResponse(e); 
          //解析响应结果 eg:JsonObjectRequest.parseNetworkResponse 解析为Json 
          request.addMarker("network-parse-complete"); 
          if(request.shouldCache() && volleyError1.cacheEntry != null) { 
            //缓存结果 
            this.mCache.put(request.getCacheKey(), volleyError1.cacheEntry); 
            request.addMarker("network-cache-written"); 
          } 
          request.markDelivered(); 
         //result->hasHadResponseDelivered()==true  
          this.mDelivery.postResponse(request, volleyError1); 
        } 
      } 
    } catch (VolleyError var7) { 
      var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); 
      this.parseAndDeliverNetworkError(request, var7); 
    } catch (Exception var8) { 
      VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()}); 
      VolleyError volleyError = new VolleyError(var8); 
      volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
      this.mDelivery.postError(request, volleyError); 
    } 
  }
}

BasicNetwork.performRequest(request)又是怎么获取结果的呢?

public NetworkResponse performRequest(Request<?> request) throws VolleyError { 
  long requestStart = SystemClock.elapsedRealtime(); 
  while(true) { 
    HttpResponse httpResponse = null; 
    Object responseContents = null; 
    Map responseHeaders = Collections.emptyMap(); 
    try { 
      HashMap e = new HashMap(); 
      this.addCacheHeaders(e, request.getCacheEntry()); 
      httpResponse = this.mHttpStack.performRequest(request, e);     
      //Boss来了,It's here; 
      StatusLine statusCode1 = httpResponse.getStatusLine(); 
      int networkResponse1 = statusCode1.getStatusCode(); 
      ...
    }
  }
}

具体的就不在这里说明了。那么如果想用Volley的上层,OkHttp的底层,又怎么来实现呢?最终请求是通过调用HttpStack.performRequest()得到响应结果的,这个HttpStack是在实例RequestQueue时赋值的。再回头看下之前的代码。

public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 
  ... 
  if(stack == null) { 
    if(VERSION.SDK_INT >= 9) { 
      stack = new HurlStack(); 
    } else { 
      stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); 
    } 
  } 
  ...
}

所以只需要继承HttpStack(如 OkHttp3Stack),重写performRequest() 方法,实例RequestQueue时使用 OkHttp3Stack即可。 详情之后继续分析。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容