zoukankan      html  css  js  c++  java
  • Java EE 架构设计——基于okhttp3 的网络框架设计

    转载请注明出处:http://blog.csdn.net/smartbetter/article/details/77893903

    本篇文章带大家设计一套满意业务需求、代码健壮高效(高内聚低耦合)并且可拓展的网络框架。以最新的okhttp3为基础设计出高效可靠的网络缓存、多线程文件下载等架构模块。从此不局限于使用别人的框架,而步入了设计框架,让自己可以走的更远,我觉得这才是一名合格开发者所应该具备的能力。在开发中,选择一个开源框架的标准有很多,例如学习成本、文档是否齐全、github星数量、现在是否有人维护、流行程度、代码设计是否有借鉴性、代码体积等。

    首先看一下当前主流网络框架对比:

    网络框架说明开源地址
    okhttp 适用于Android和Java应用程序的HTTP+HTTP/2客户端 https://github.com/square/okhttp
    retrofit 在okhttp之上做了相应封装;解耦性,注解处理,简化代码;支持上传和下载文件;支持自动更换解析方式;支持多种http请求库 https://github.com/square/retrofit

    Http网络框架设计图:

    Http网络框架设计图

    现在很多开源框架都是使用okhttp做底层协议的网络请求。所以我们适用时代潮流,使用okhttp做网络请求,而不是用传统的HttpURLConnection。

    1.http协议

    http协议,超文本传输协议,目前使用最普遍的还是http1.1版本,不过我们也来了解下http2.0协议:

    http1.1的特点:

    1)支持客户/服务器模式
    2)简单快速,GET、POST(http的几种请求方式:Get、POST、HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS。)
    3)灵活,允许传输任意类型的数据对象。
    4)无连接,限制每次连接只处理一个请求。
    5)无状态,协议对于事务处理没有记忆能力。
    

    http2.0对比http1.1增加的特点:

    1)多路复用允许同时通过单一的HTTP/2连接发起多重的请求-响应消息,单连接多资源的方式减少服务端的链接压力,内存占用更少,连接吞吐量更
    大,由于TCP连接的减少而使网络拥塞状况得以改善,同时TCP慢启动时间的减少,使拥塞和丢包恢复速度更快;
    2)头部压缩,每次都要传输UserAgent、Cookie这类不会变动的内容,使用HPACK算法进行压缩;
    3)对请求划分优先级;
    4)服务器推送流(即Server Push技术)。
    

    http请求返回的一些响应码:

    100-101:信息提示; 200-206:成功; 300-305:重定向; 400-415:客户端错误; 500-505:服务器错误。
    

    2.网络框架的基石 okhttp3

    需要添加 Maven 依赖:

    <!-- okhttp3 -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>3.9.0</version>
    </dependency>

    关于okhttp的使用方法官方文档中已经给出了说明:http://square.github.io/okhttp/,下面我们将对okhttp做出补充说明。

    1.okhttp的同步请求和异步请求

    同步请求(使用 JUnit 编写测试方法):

    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder().url(url).build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            System.out.println(response.body().string());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    异步请求:

    System.out.println(Thread.currentThread().getId());
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder().url(url).build();
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                System.out.println(Thread.currentThread().getId());
            }
        }
    });

    2.okhttp请求头和响应头的实际应用

    请求头可参考:http://tools.jb51.net/table/http_header

    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
            .url("http://www.baidu.com")
            .addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) " +
                    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36")
            .addHeader("Range", "bytes=2-")
            .addHeader("Accept-Encoding", "identity")
            .build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            Headers headers = response.headers();
            for (int i = 0; i < headers.size(); i++) {
                System.out.println(headers.name(i) + " : " + headers.value(i));
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    3.okhttp get请求和post请求

    get用于信息获取,而且应该是安全的和幂等的。

    请求特点
    get get用于信息获取,而且应该是安全的和幂等的,所谓安全的意味着该操作用于获取信息而非修改信息,不会影响资源的状态。幂等意味着对同一URL的多个请求应该返回同样的结果。
    post post用于修改服务器上的资源,需要注意的是post必须要到Form(表单)。

    get请求:

    OkHttpClient client = new OkHttpClient();
    HttpUrl httpUrl = HttpUrl.parse("http://localhost:8080/web/HelloServlet")
            .newBuilder()
            .addQueryParameter("username", "user")
            .addQueryParameter("password", "user123")
            .build();
    Request request = new Request.Builder().url(httpUrl.toString()).build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            System.out.println(response.body().string());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    post请求:

    默认地,表单数据会编码为”application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码,空格转换为”+”,特殊符号转换为ASCII HEX值。

    除此之外,还有一个multipart/form-data,它不对字符编码,在使用包含文件上传的控件的表单时,必须使用该值,例如拍照上传等。okhttp已经对multipart/form-data进行了相应的封装,简化了很多操作。

    OkHttpClient client = new OkHttpClient();
    FormBody body = new FormBody.Builder()
            .add("username", "user") //如果包含中文需要调用addEncoded方法进行转码,而不是add方法
            .add("age", "18")
            .build();
    Request request = new Request.Builder().url("http://localhost:8080/web/HelloServlet").post(body).build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            System.out.println(response.body().string());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    3.okhttp上传文件

    1.mutipart协议上传文件

    自己实现上传文件的功能相对又复杂。第一,你需要了解文件上传之后http请求过程当中的具体协议的含义是什么,因为你需要自己去封装中间的一些参数信息;第二,你需要自己去搭建一个web服务。

    后端接口可以参照 http://blog.csdn.net/smartbetter/article/details/52056377 中使用FileUpload实现文件上传部分。

    2.okhttp使用mutipart协议上传文件

    RequestBody imageBody = RequestBody.create(MediaType.parse("image/jpeg"), 
            new File("/Users/macos/Desktop/temp.jpg"));
    MultipartBody body = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("name", "zhangsan")
            .addFormDataPart("filename", "temp.jpg", imageBody)
            .build();
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder().
            url("http://192.168.1.20:8080/example/upload/api/file").post(body).build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            System.out.println(response.body().string());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    4.okhttp数据缓存

    1.http协议当中缓存的原理和关键字段

    http缓存的原理

    缓存关键字段:

    Expires:指示响应内容过期的时间,格林威治时间GMT
    Catche-Control:
        | no-cache:无缓存指令
        | max-age:如果缓存只是用来和服务器做验
        | only-if-cached:有时你会想显示可以立即
        | max-stale:设置最长过期时间来允许过期的response响应(有时候过期的response比没有response更好)。
    
    Last-Modified:判断客户端数据和服务端数据有没有变化。如果相同表示没有人修改,如果修改则Last-Modified事件发生变化。
    ETag:对服务器返回的整个response做了相应的编码处理,得到了一个加密的值。
    Date:
    If-Modified-Since:请求头中标识的,客户端存取的该资源最后一次修改的时间,同Last-Modified。
    If-None-Match:请求头中标识的。
    

    2.okhttp实现数据缓存

    okhttp缓存相关类:CacheInterceptor、CacheStrategy、Cache、DiskLruCache。

    int maxCacheSize = 10 * 1024 * 1024;
    Cache cache = new Cache(new File("/Users/macos/Desktop/source"), maxCacheSize);
    OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
    
    Request request = new Request.Builder().url("http://www.163.com/")
            .cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build())
            .build();
    
    Response response = client.newCall(request).execute();
    
    String body1 = response.body().string();
    System.out.println("network response " + response.networkResponse());
    System.out.println("cache response " + response.cacheResponse());
    
    System.out.println("**************************");
    
    Response response1 = client.newCall(request).execute();
    
    String body2 = response1.body().string();
    System.out.println("network response " + response1.networkResponse());
    System.out.println("cache response " + response1.cacheResponse());

    我们运行这个测试类,显示如下,可以看到,先请求了network,后请求了cache:

    数据缓存

  • 相关阅读:
    这么香的Chrome插件,你都安装了吗?
    Android frameworks源码StateMachine使用举例及源码解析
    数据结构与算法系列七(队列)
    openoffice下中文乱码问题解决
    linux yum命令详解
    linux下Redis的安装
    ason 和 Java 对象转化示例
    java将office文档pdf文档转换成swf文件在线预览
    MyBatis SQL xml处理小于号与大于号
    Fisher 线性判别
  • 原文地址:https://www.cnblogs.com/DaTouDaddy/p/7498785.html
Copyright © 2011-2022 走看看