《Android中高级工程师面试指南 — 网络协议和网络框架面试讲解 — OKHttp》

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jiangguangchao/article/details/78478326

前言

  Volley、OKHttp和Retrofit都是Android开发中经常使用的网络框架。Volley内部是使用HttpClient实现的,Retrofit是基于OKHttp实现的,同时在Android6.0之后,Google把HttpClient从SDK中移除,这就导致OKHttp(包括Retrofit)成为了Google官方承认的唯一的网络请求框架。所以在Android开发面试中OKHttp几乎成为了必考的网络请求框架。
  对于初级Android开发工程师,只需要掌握OKHttp的使用,对于中高级Android开发工程师,还必须掌握OKHttp的源码实现,是否读过源码也是检验面试者技术水平经常使用的方式。
  OKHttp网络框架这块设计的知识点包括OKHttp的使用、OKHttp的源码分析,OKHttp的自定义封装。

一、OKHttp的使用

Q: 请简述如何使用OKHttp发起网络请求?
A: 使用OKHttp发起网络请求的步骤如下:
  1、新建OKHttpClient
  2、根据请求的URL新建Request对象
    请求过程中所使用的参数放在Request对象的RequestBody中
  3、使用Request对象新建Call对象
  4、同步请求执行call.execute(),异步请求执行call.enqueue
  5、获取请求执行结果对象Response 并解析

  详解:一般面试时说出上述的步骤就可以了,以下是OKHttp使用的详细教程,供大伙参考。

1.1 OKHttp引入

  使用Android Studio引入OKHttp,只需要配置gradle文件即可,如下:

       compile 'com.squareup.okhttp3:okhttp:3.2.0' 
       compile 'com.squareup.okio:okio:1.7.0‘

  同时,需要加上网络请求的权限。

1.2 使用OKHttp发起同步请求

  调用Call对象的execute()方法,代码如下:

String url = "http://www.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        try {
            Response response = call.execute();
            //TODO 解析response
        } catch (IOException e) {
            e.printStackTrace();
        }

1.3 使用OKHttp发起异步请求

  调用Call对象的enqueue()方法,代码如下:

 String url = "http://www.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //我在子线程中
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //我在子线程中
            }
        });

  注意Callback的回调在子线程中,因此不能直接更新UI。

  由于Android4之后不允许在主线程中发起网络请求,因此实际使用中,绝大部分情景都是使用OKHttp的异步请求,后面的介绍也以异步请求为例。

1.4 使用OKHttp发起GET请求

  代码如下:

 String url = "http://www.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //我在子线程中
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //我在子线程中
            }
        });

1.5 使用OKHttp下载文件

  代码如下:

 String url = "http://www.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //我在子线程中
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //TODO 解析流并保存成文件
            }
        });

  下载文件使用普通的GET请求即可,只需在返回结果中根据获取的流保存成文件即可。

1.6 使用OKHttp发起POST请求

  使用OKHttp发起POST的请求和发起GET请求的流程是一样的,只不过在创建Request对象时需要传入一个RequestBody对象,RequestBody对象保存需要提交给客户端的表单数据。

  RequestBody需要指定Content-Type,常见的数据格式有三种:
  application/x-www-form-urlencoded  数据是个普通表单
  multipart/form-data  数据里有文件
  application/json  数据是个json串

1.7 使用OKHttp发起POST请求并上传键值对

  代码如下:

  String url = "http://192.168.1.105/user/login";
        OkHttpClient okHttpClient = new OkHttpClient();

        RequestBody body = new FormBody.Builder()
                .add("login_username", "jgc")
                .add("login_password", "123456").build();
        Request request = new Request.Builder().url(url).post(body).build();

        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "failure");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "success");
            }
        });

  FormBody继承自RequestBody并指定了数据类型为application/x-www-form-urlencoded

1.8 使用OKHttp发起POST请求并上传JSON串

  代码如下:

 String url = "http://192.168.1.105/user/login";
        OkHttpClient okHttpClient = new OkHttpClient();

        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "你的json");
        Request request = new Request.Builder().url(url).post(body).build();

        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "failure");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "success");
            }
        });

  创建RequestBody时指定数据类型为application/json即可。

1.9 使用OKHttp上传文件

  代码如下:

 String url = "http://192.168.1.105/user/upload";
        OkHttpClient okHttpClient = new OkHttpClient();

        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM); //设置类型
        builder.addFormDataPart("UploadForm", file.getName(), RequestBody.create(MediaType.parse("image/png"), file));   
        RequestBody body = builder.build();

        Request request = new Request.Builder().url(url).post(body).build();

        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "failure");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "success");
            }
        });

  使用MultipartBody.Builder来创建RequestBody对象,需要指定数据类型并且设置表单数据部分。

Q: 如何使用OKHttp对Cookie进行处理?
A: 使用OKHttp进行Cookie处理,有两种方式,第一种是手动处理,在callback回调的response对象中,通过response.headers()可以获取所有头信息,包括Cookie信息。这时我们可以将Cookie保存到本地,下次发起网络请求时再将Cookie信息加在头部。
  第二种是使用OKHttp提供的Cookjar,具体代码如下:

builder = new OkHttpClient.Builder();  
builder.cookieJar(new CookieJar() {  
    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();  

    @Override  
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {  
        cookieStore.put(url.host(), cookies);  
    }  

    @Override  
    public List<Cookie> loadForRequest(HttpUrl url) {  
        List<Cookie> cookies = cookieStore.get(url.host());  
        return cookies != null ? cookies : new ArrayList<Cookie>();  
    }  
});  
okHttpClient = builder.build();  

  其中,saveFromResponse()会在接收服务器响应时执行,这时可以保存Cookie信息,loadForRequest()会在发起网络请求时执行,这是可以添加已保存的Cookie信息。

二、OKHttp源码分析

Q: 请介绍一下OkHttp发起网络请求的整体流程?
A: 下面这张图要记住:
OKHttp网络请求流程图

  注:OKHttp发起网络请求的流程都在上面的这张图中,大家伙面试的时候可以再过一遍源代码,代码不复杂,很容易看懂。其中几个点需要大家关注一下:
  1、OKHttp发起异步请求时会进行任务调度,主要涉及Dispatcher这个类,Dispatcher类中维护了一些常量用于控制并发请求,其中很重要的一个常量就是最大并发请求数—64,这个常量一定要记住。
  2、OKHttp在发起真正的网络请求之前,会通过OKHttp的拦截器链机制拼接请求,拦截器的作用也需要记住:拦截器主要用来添加、移除、转换请求或者响应的头部信息。比如将域名替换为ip地址,将请求头中添加host属性,也可以添加我们应用中的一些公共参数,比如设备id、版本号等等。
  3、关于OKHttp网络请求流程这块,可以参考刘望舒的一篇博客:http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html

Q: OkHttp对于网络请求都有哪些优化,如何实现的?
A: OKHttp相比原生的HttpUrlConnection主要做了如下优化:
  1、支持HTTP2/SPDY
  2、socket自动选择最好路线,并支持自动重连
  3、拥有自动维护的socket连接池,减少握手次数,降低响应延迟
  4、拥有队列线程池,轻松写并发
  5、拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
  6、基于Headers的缓存策略,避免重复的网络请求

  关于OKHttp的Socket连接可以参考刘望舒的这篇博客,这部分的知识太过底层,面试者明白基本原理即可。
http://liuwangshu.cn/application/network/8-okhttp3-sourcecode2.html

Q: 请简单说一下OKHttp的源码实现?
A: 回答这道题的时候,面试者可以先简述一下OKHttp发起网络请求的流程。根据流程来说OKHttp的源码实现,下面的回答仅供大家参考。
  OKHttp的网络请求分为同步和异步两种,异步请求涉及到任务调度机制,主要涉及的类是Dispatcher。Dispatcher类中维护了一个线程池用于执行网络请求,同时维护了3个队列用于存放不同的网络请求。
  OKHttp发起网络请求之前,会通过自己的拦截器链机制,拼接相关的网络请求。拦截器可以用来添加、移除、转换请求或者响应的头部信息。
  OKHttp拼接完请求之后,会检查一下自己的缓存。如果有缓存并且可用则用缓存的数据并更新缓存,否则就用网络请求返回的数据。OKHttp发起网络请求并接受服务器相应,主要使用HttpEngine的sendRequest()和readResponse()方法,也是在这两个方法中进行的缓存处理。
  由于Http协议是基于TCP协议的,OKHttp对于网络请求的底层也做了一些优化,也就是对Socket连接。主要是通过OKHttp的复用连接池机制,通过对Socket连接的复用,可以减少TCP握手次数,提高网络请求的效率,减少网络请求延迟。

  注:本篇博客开始,后期会推出《Android中高级工程师面试指南》系列博客,该系列博客收录一些Android中高级工程师面试时经常遇到的面试题,内容包括:Android基础知识面试讲解、消息机制和多线程面试讲解、网络协议和网络框架面试讲解、View相关面试讲解、性能优化面试讲解、设计模式和架构设计面试讲解、Android源码相关面试讲解、前沿技术面试讲解。希望能够帮助各位读者找到心仪的工作。
  注:在Android中高级工程师面试的时候,基础知识一般会问的比较细,面试官会通过一些技术细节来判断你的技术水平,所以基础知识这部分一定要好好准备,尤其是一些细节。
  注:能力有限,水平一般,如果文章中有错误,欢迎大家留言指正。如果大家觉得文章写的不错,对技术水平的提升还有点帮助,扫描下面的二维码,打赏笔者一瓶水钱也是极好的~~~
这里写图片描述

猜你喜欢

转载自blog.csdn.net/jiangguangchao/article/details/78478326