Android 网络请求框架okhttp学习笔记

——谁不是一边受伤一边成长,谁不是一面流泪一面坚强。

前言

这篇给Okhttp做个记录。主要还是对自己阅读源码后的一个总结。当然距离上次看过源码已经1年多。写这篇的原因还是担心过太久又忘了。 所以复习并总结一下。本篇不会有太多的拓展,主要以介绍性为主。

一、简介

github地址:GitHub

(1)是什么

——是目前最火的开源网络请求框架。有人可能会想到Retrofit,当然Retrofit也是基于okhttp的一个二次封装库。

(2)有什么用

——提供更方便的网络请求方式。通过该库可以更加简洁的获取到网络数据。

二、怎么用

步骤:

(1)添加依赖

 implementation "com.squareup.okhttp3:okhttp:3.5.0"

(2)创建OkHttpClient

OkHttpClient okHttpClient = new OkHttpClient();

(3)构建Request请求信息

//构建请求信息:连接请求url 请求方法method 请求头部headers 请求体body 标签tag

Request request = new Request.Builder().url("xxxxxx").get().build();

(4)创建Call对象

Call call = okHttpClient.newCall(request);

(5)创建同步请求

        try {
            Call call = okHttpClient.newCall(request);
            Response execute = call.execute();
            String success = execute.body().toString();
            Log.e(TAG, "onCreate: "+success );
        } catch (IOException e) {
            e.printStackTrace();
        }

(6)创建异步请求

okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

(7)总结

        //创建OkhttpClient
        OkHttpClient okHttpClient = new OkHttpClient();
        //构建请求信息:连接请求url 请求方法method 请求头部headers 请求体body 标签tag
        Request request = new Request.Builder().url("xxxxxx").get().build();
        //同步
        try {
            Call call = okHttpClient.newCall(request);
            Response execute = call.execute();
            String success = execute.body().toString();
            Log.e(TAG, "onCreate: "+success );
        } catch (IOException e) {
            e.printStackTrace();
        }
        //异步
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

三、原理

(1)创建OkHttpClient:OkHttpClient okHttpClient = new OkHttpClient();

//1.创建OkhttpClient 有两种方式
(1)OkHttpClient okHttpClient = new OkHttpClient();

(2)OkHttpClient build = new OkHttpClient.Builder().build();

第一种OkhttpClient 采用默认配置信息
第二种通过Builder 可自定义配置信息 

  //默认配置 Builder()
  public OkHttpClient() {
    this(new Builder());
  }

  //OkhttpClient 配置信息
  public Builder() {
      //创建Dispatcher: 该对象内部维护着一个线程池,
      //管理OkhttpClient的线程操作。通过该对象可执行异步操作。
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;//定义默认协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS;//定义连接方式
      proxySelector = ProxySelector.getDefault();//使用默认代理服务
      cookieJar = CookieJar.NO_COOKIES;//定义 Cookie
      socketFactory = SocketFactory.getDefault();//使用默认的SocketFactory
      hostnameVerifier = OkHostnameVerifier.INSTANCE;//定义默认证书
      certificatePinner = CertificatePinner.DEFAULT;//定义不受信任的证书
      //设置网络身份验证
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      //连接池:管理HTTP和HTTP/2连接的重用,以减少网络延迟
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      //定义HTTPS 和 HTTP 之间的重定向
      followSslRedirects = true;
      followRedirects = true;
      //定义是否失败重连
      retryOnConnectionFailure = true;
       //定义连接超时连接
      connectTimeout = 10_000;
        //定义读取超时连接
      readTimeout = 10_000;
      //定义写入超时连接
      writeTimeout = 10_000;
       //定义Web socket ping 间隔
      pingInterval = 0;
    }

进阶:还可以通过addNetworkInterceptor(Interceptor interceptor)与addInterceptor(Interceptor interceptor)添加自定义拦截器

addInterceptor()方法添加的拦截器是放在addNetworkInterceptor()前面的。

addNetworkInterceptor() 方法添加的网络拦截器,则是在非 WebSocket 请求时。

添加在 ConnectInterceptor 和 CallServerInterceptor 之间的。

addInterceptor(应用拦截器):
1,不需要担心中间过程的响应,如重定向和重试.
2,总是只调用一次,即使HTTP响应是从缓存中获取.
3,观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
4,允许短路而不调用 Chain.proceed(),即中止调用.
5,允许重试,使 Chain.proceed()调用多次.
addNetworkInterceptor(网络拦截器):
1,能够操作中间过程的响应,如重定向和重试.
2,当网络短路而返回缓存响应时不被调用.
3,只观察在网络上传输的数据.
4,携带请求来访问连接.

(2)构建Request请求信息:Request request = new Request.Builder().url("xxxxxx").get().build();

//请求信息
public final class Request {
  final HttpUrl url; //配置url
  final String method;//配置请求方法
  final Headers headers;// 配置请求头部
  final RequestBody body;//配置请求体
  final Object tag; //标记请求
}

//使用默认get请求 与默认头部
public Builder() {
   this.method = "GET";
   this.headers = new Headers.Builder();
}

//创建Request 请求
public Request build() {
   if (url == null) throw new IllegalStateException("url == null");
   return new Request(this);
}
//配置Request 信息
Request(Builder builder) {
   this.url = builder.url;
   this.method = builder.method;
   this.headers = builder.headers.build();
   this.body = builder.body;
   this.tag = builder.tag != null ? builder.tag : this;
}

(3)OkHttpClient 关联 Request: Call call = okHttpClient.newCall(request);

 /**
 * 1.创建 RealCall()
 */
 @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

/**
 * 2\. RealCall 持有 OkHttpClient对象与Request对象 并创建 重定向拦截器
 * 主要作用是发起请求
 */
  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

/**
 * 3.重定向拦截器  持有OkHttpClient 对象
 */
 public RetryAndFollowUpInterceptor(OkHttpClient client, boolean forWebSocket) {
    this.client = client;
    this.forWebSocket = forWebSocket;
  }

(4)发起请求

//同步请求

 //调用 RealCall 对象execute() 发起同步请求
@Override public Response execute() throws IOException {
    synchronized (this) {
//判断是否在执行
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
//将一些堆栈跟踪的信息存储于重定向拦截器
    captureCallStackTrace();
    try {
//将Call添加到同步运行队列中
 client.dispatcher().executed(this);
//通过okHttpClient调用调度器(dispatcher)执行拦截器链 ——> (1) 
Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

// (1)通过okHttpClient调用调度器(dispatcher)执行拦截器链:
// 主要作用:连接服务器,处理请求体与响应体。最终获取到Response 数据。
// okHttpClient 核心方法
Response getResponseWithInterceptorChain() throws IOException {
    // 1.创建容器存储拦截器
    List<Interceptor> interceptors = new ArrayList<>();
    // 2.添加自定义拦截器
    interceptors.addAll(client.interceptors());
    // 3.添加重定向拦截器 主要作用
    //(1)根据异常结果或者响应结果判断是否要进行重新请求
    interceptors.add(retryAndFollowUpInterceptor);
    // 4.添加网络连接桥拦截器 主要作用:
    //(1)将用户构建的一个 Request 请求转化为能够进行网络访问的请求
    //(2)将网络请求回来的响应 Response 转化为用户可用的 Response
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    // 5.添加缓存拦截器 主要作用:
    // (1)创建缓存策略 缓存响应数据
    interceptors.add(new CacheInterceptor(client.internalCache()));
    // 6.添加连接拦截器 主要作用:
    // (1)与服务端 建立连接,并且获得通向服务端的输入和输出流对象
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        // 7.添加 自定义网络拦截器
      interceptors.addAll(client.networkInterceptors());
    }
    // 8.添加 服务回调拦截器 主要作用:
     // (1)使用 HttpStream 与服务器进行数据的读写操作的
    interceptors.add(new CallServerInterceptor(forWebSocket));
     // 9.创建拦截器链 并启动
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

//异步请求

//调用该方法 执行异步请求
@Override public void enqueue(Callback responseCallback) {
    //判断是否重复执行
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    //将一些堆栈跟踪的信息存储于重定向拦截器
    captureCallStackTrace();
    // 详情——> (1)   (2)
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

(1)异步请求策略
  synchronized void enqueue(AsyncCall call) {
    //判断运行的异步线程数是否小于最大的请求数    核心线程数小于最大的运行数
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    // 运行的异步线程数未满 则执行新的任务caLl
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
    // 运行的异步线程数达到最大值 则将新的任务添加到等待的队列中
      readyAsyncCalls.add(call);
    }
  }

(2)创建AsyncCall 继承NamedRunnable 对象
并执行NamedRunnable 的run()

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
        //——> (3)
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }
(3) 最终执行AsyncCall 的 execute() 
 @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        //同样执行拦截器链 获取到Response 
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

通过源码可以清楚的了解到,OKHTTP 该库获取网络请求数据的核心实现是通过责任链模式。

由各个拦截器协作完成对网络服务器的数据获取。

重要的过程还是要通过了解各个拦截器实现。才能清楚如何连接网络。并获取到数据。

这里就不详细记录。 谢谢各位浏览。 。。。

四、内容推荐

若您发现文章中存在错误或不足的地方,希望您能指出!

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

推荐阅读更多精彩内容