优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
来源:互联网 发布:照片打分软件 编辑:程序博客网 时间:2024/05/01 06:07
如今Android开发中Okhttp已成为主流网络框架,内置丰富、全面、强大的网络请求功能,也为开发者提供了api,但是在项目开发中的大量使用,会出现api的重复调用、代码冗杂等现象。应当封装一个适用于项目的网络框架,便于使用。
此系列文章旨于:基于okhttp3原始框架来设计封装一个满足业务需求、扩展性强、耦合度低的网络框架。具体框架功能为:
- 封装基本的网络请求
- 扩展其对数据库的支持
- 对多文件上传、多线程文件下载的支持
- 对Json数据解析等功能的支持
此系列文章将详细记录其封装过程,从底层框架的选择、基础知识、设计理念到架构分析、封装代码,自顶向下来完成网络框架的设计封装,最后附上源码,下面开始!
一. 主流网络框架分析与选择
1. 常用网络框架介绍
在Android网络框架并不成熟的时候,开发者在开发过程中往往会遇到几大难题:如何去访问网络数据?如何做本地存储?如何做图片缓存等类似问题。随着Android不断发展,继而推出了以下几大常用框架:
(1)Volley
谷歌研发的一款底层基础网络框架,Volley在Android整个发展中是出现较早的,早期的项目使用的便是Volley框架,但是以当今的角度去分析它还是有些不足的地方,例如不支持文档、文件的下载,更倾向于轻量级数据的请求。
优点:
- 默认Android2.3及以上基于HttpURLConnection,2.3以下使用基于HttpClient。
- 符合Http 缓存语义 的缓存机制(提供了默认的磁盘和内存等缓存)。
- 请求队列的优先级排序(网络框架的基本必备)。
- 提供多样的取消机制。
- 提供简便的图片加载工具(其实图片的加载才是我们最为看重的功能)。
缺点:
- 不能下载文件。
(2)Android-async-http
提供了Http请求的大部分功能,绝对满足网络请求的各种需求,支持智能重试、gzip压缩等功能,是一个功能全面的网络框架,而且在github上start星数是Volley的两倍。
优点:
- 在匿名回调中处理请求结果
- 在UI线程外进行http请求
- 文件断点上传
- 智能重试
- 默认gzip压缩
- 支持解析成Json格式
- 可将Cookies持久化到SharedPreference
缺点:
- 已停止维护更新,新功能需自己实现
(3)Afinal 和 XUtils
国人研发的框架,不仅仅提供网络请求的功能,还支持数据库管理、图片下载缓存等功能。虽然功能丰富,但是没有只专于网络请求的框架功能细致、全面,后期维护成本很高。此框架较适用于小型快速开发。
四大模块功能:
- 数据库模块: android中的orm框架,使用了线程池对sqlite进行操作。
- 注解模块: android中的ioc框架,完全注解方式就可以进行UI绑定和事件绑定。无需findViewById和setClickListener等。
- 网络模块:通过httpclient进行封装http数据请求,支持ajax方式加载,支持下载、上传文件功能。
- 图片缓存模块:通过FinalBitmap,imageview加载bitmap的时候无需考虑bitmap加载过程中出现的oom和android容器快速滑动时候出现的图片错位等现象。
(4)Okhttp
由square公司研发(开源界最著名的两个公司:Square、Facebook),OkHttp是一款优秀的HTTP框架,大致功能为:
- 支持get请求和post请求,支持基于Http的文件上传和下载
- 支持加载缓存图片
- 支持下载文件透明的GZIP压缩
- 支持响应缓存避免重复的网络请求
- 支持使用连接池来降低响应延迟问题。
就目前而言,Okhttp网络请求框架已被广泛应用成为主流框架,而且在Android6.0时已被集成到系统中默认底层协议,可以看出谷歌对Okhttp的满意度。
(5)Retrofit
本质上就是在Okhttp上做了相应的封装,网络底层交互还是Okhttp,封装如下:
- 支持okhttp
- 注解处理,简化代码
- 支持上传和下载文件
- 支持自己更换解析方式
- 支持多种http请求库
2. 网络框架选择标准
参考标准:
- 学习成本
- 文档是否齐全
- github 星数量
- 现在是否有人维护
- 流行程度
- 代码设计是否有借鉴性
- 代码体积
选择
综合以上考虑,本次封装的网络框架是基于Okhttp3,第二点从架构设计的层面来分析本次封装的网络框架,此点较为重要!
二. 架构设计分析 ★ ★ ★ ★ ★
在Android开发当中往往会涉及到设计模式的采用,例如MVC、MVP,MVVM等,但是在这个网络框架设计并未采用任何设计模式,主要是按照分层次的划分来实现解耦的目的,方便于此框架以后拓展与修改。
如上图所示,此框架可以分为三个层次:
第一层:便于框架扩展,第一层即最底层是Http Interface和Abstact,例如Http中的Headers、Request、Response等通用的原生接口。
第二层:有了第一层请求接口定义,便于第二层对接口的实现,此框架采用两种方式对接口进行实现,分别是Okhttp和原生的HttpURLConnection。通过这两个相关的API去实现整个Http请求和响应的过程,若还想要做相应的拓展,采用别的第三方http请求库,在此处可增加。(已经预先在第一层定义了足够多的接口实现网络请求的回调,第一层可无需修改)对于整个上层业务来说,无需直接接触到底层Okhttp、HttpURLConnection具体实现,所以提供二次封装的 HttpProvider ,暴露接口给上层调用。(具体底层是调用Http还是HttpURLConnection取决于配置,首先判断Okhttp依赖在项目中是否存在,若有则主要采用Okhttp来进行网络请求,否则采用HttpURLConnection)
第三层:即最上层由 Workstation 和Convert组成。Workstation 的中文意思是工作站,用来处理一些线程的调度分发和任务的队列,之所以将它设计在最上层,因为整个多线程、队列机制是与业务层紧密相关的。Convert是为上层开发者提供了更好的接口封装,用于接口返回类型转换、数据解析,例如json、xml等。
三. Http协议基础内容
1. 什么是Http协议?
HTTP(Hypertext Transfer Protocol),即超文本传输协议。是WWW浏览器和WWW服务器之间的应用层通讯协议。HTTP协议是基于TCP/IP之上的协议,它不仅保证正确传输超文本文档,还确定传输文档中的哪一部分,以及哪一部分内容首先显示(如文本先于图形)。
2. Http 版本区别
3. Http的请求方式总结
4. Http协议的特点
- 支持客户/服务器模式。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、 HEAD、POST。每种方法规定了客户与服务器联系的类型不同。 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活: HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 无连接: 无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求, 并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态: HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。 缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每 次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
5 . 请求头信息
这里只介绍举例部分重要的请求响应头,关于更详细信息可看:
http://tools.jb51.net/table/http_header
6 . 响应头信息
7 . 状态码信息
HTTP状态码分类
较常用状态码
- 200 - 请求成功
- 301 - 资源(网页等)被永久转移到其它URL
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
四. Okhttp使用解析
以上基础HTTP网络知识在后续的网络框架编码中会涉及到,所以需要稍作了解。在学习以上知识点后,下面举例介绍使用Okhttp3框架请求网络常用的方式,这里只介绍后续封装使用到的请求方式。(默认读者有基本使用Okhttp3基础,网上使用教程很多,可自行搜查)
1. 同步请求和异步请求
大多数情况异步请求较于同步请求更为广泛,因为同步请求的过程中容易阻塞到线程,而异步请求通过内部队列维护完美避免此问题,并且在回调中处理请求结果更符合需求。典型的使用方法如下:
public class AsyncHttp { /* * 同步请求(会阻塞到线程) * */ public static void sendRequest(String url) { 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(); } } /* * 异步请求 * */ public static void sendAsyncRequest(String url) { 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()); } } }); } public static void main(String args[]) { System.out.println(0/100.0); sendRequest("http://www.baidu.com");// sendAsyncRequest("http://www.baidu.com"); }}
2. http请求头与响应头请求
在第二节中介绍了Http协议及请求头、响应头等相关知识,在使用Okhttp3框架请求网络时可以显式调用、设置、获取相关信息,典型使用如下:
public class HeadHttp { public static void main(String args[]) { String str = "1234"; System.out.println(str.substring(0,str.length() - 3)); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder(). url("http://www.imooc.com/static/sea-modules/seajs/seajs/2.1.1/sea.js"). 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();// System.out.println(response.body().string()); System.out.println("size=" + response.body().contentLength()); System.out.println("type=" + response.body().contentType()); 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. get请求之添加参数
get请求为Http的请求方式中最常用的一种,访问URL获取资源,大多数情况url通过参数组成而来,以下代码则介绍通过添加参数的方式来获取最终url进行get请求,典型使用如下:
public class QueryHttp { public static void main(String args[]) { OkHttpClient client = new OkHttpClient(); HttpUrl httpUrl = HttpUrl.parse("https://api.heweather.com/x3/weather"). newBuilder(). addQueryParameter("city", "beijing"). addQueryParameter("key", "d17ce22ec5404ed883e1cfcaca0ecaa7"). build(); String url = httpUrl.toString(); System.out.println(httpUrl.toString()); 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(); } }}
4. post请求之添加参数
上一点介绍完get请求后,必然联想到post请求,get请求中的添加的参数信息全部显示在url上,根本上决定了get请求只适用于资源的资源获取显示,需要传送重要数据给服务器时需要使用post请求方式,不仅对传送参数大小限制少,而且更为安全,典型的应用有登录注册。使用方式如下:
public class PostHttp { public static void main(String args[]) { new Thread() { @Override public void run() { OkHttpClient client = new OkHttpClient(); FormBody body = new FormBody.Builder().add("username", "nate") .add("userage", "99").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(); } } }.start(); }}
5. martipart 上传文件(MP4)
上一点传送给服务器的数据仅限于简单的字段,但是需求中常涉及到传输文件,所以需要使用martipart 来使用类似需求,典型使用方式如下:
public class MultipartHttp { public static void main(String args[]) { RequestBody imageBody = RequestBody.create(MediaType.parse("image/jpeg"), new File("/Users/nate/girl.jpg")); MultipartBody body = new MultipartBody.Builder(). setType(MultipartBody.FORM). addFormDataPart("name", "girl"). addFormDataPart("filename", "girl.jpg", imageBody).build(); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder(). url("http://192.168.1.6:8080/web/UploadServlet").post(body).build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { System.out.println(response.body().string()); } } catch (IOException e) { e.printStackTrace(); } }}
另外,有兴趣了解Okhttp3源码图解分析缓存机制的读者可看此篇博客,是由此系列分离出来的知识点,了解学习对后续封装框架颇有帮助:
以 Okhttp3源码 为例 —— 图解 缓存机制 的原理和实现(上)
以 Okhttp3源码 为例 —— 图解 缓存机制 的原理和实现(下)
四. 小结
此篇文章的第二大点,也就是架构设计分析 最为重要,这是将要设计的网络框架的精髓支撑。要设计一个适用、合理的框架,解耦与拓展是相当重要的,不同分层的功能的实现相辅相成,应当被注重!
以上内容只是此系列的一个开始,Http网络协议的基础了解和Okhttp3基本方法的解析,做好铺垫之后,下一篇文章开始解析多线程功能的设计和实现。
期待下篇文章出炉 ~
希望对你们有帮助 :)
- 优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
- 优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
- 优雅设计封装基于Okhttp3的网络框架(二):多线程下载功能原理设计 及 简单实现
- 优雅设计封装基于Okhttp3的网络框架(四):多线程下载添加数据库支持(greenDao)及 进度更新
- 优雅设计封装基于Okhttp3的网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换
- 优雅设计封装基于Okhttp3的网络框架(三):多线程下载功能核心实现 及 线程池、队列机制、终止线程解析
- 优雅设计封装基于Okhttp3的网络框架(五):多线程、单例模式优化 及 volatile、构建者模式使用解析
- 基于OkHttp3封装网络请求框架
- Java Develop——基于 okhttp3 的网络框架设计
- android网络请求组件(一)OkHttp3的封装使用
- OkHttp3网络协议的使用
- okhttp3的网络请求框架
- Android网络请求框架----Okhttp3完全解析(2),封装框架
- 【快速开发】OKhttp3+fastjson 网络数据的请求与解析
- retrofit2+okhttp3+rxjava网络封装
- 火热轻量级网络框架okhttp3的使用
- 使用OkHttp3网络请求的错误解析
- Android-网络框架01OKHttp3
- 缓存策略之LRU实现(基于双链表实现)
- OpenOffice格式转换中文乱码终极解决方案
- Web前端常用问题--1
- 制作如下所示页面。(东北林业大学WEB课程网页)
- Filter——实现敏感字拦截
- 优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
- NIO学习笔记——Buffer的创建与复制
- AbsListView中item重用机制
- 单词切分
- JS获取当前日期前后的日期
- Java基础02关键字与数据类型
- CSDN日报20170621——《开发者,只有被裁,没有退休》
- hihoCoder太阁最新面经算法竞赛题解(5)
- 【PAT甲级】1073. Scientific Notation (20)