使用Retrofit2封装适用于组件化项目的网络库
来源:互联网 发布:两个数据库数据同步 编辑:程序博客网 时间:2024/06/07 13:03
- 1URL的简单构成
- 2Http中GET和POST的区别
- 3Retrofit 概览
- 1Url配置
- 2Retrofit注解
- 3请求参数注解的使用方法
- 4ConverterFactory 数据转换工厂
- 4使用Retrofit进行网络请求
- 5Retrofit二次封装Builder模式
- 1 Builder模式的定义
- 2 使用Builder的目的
- 3 具体的封装过程
- 先创建一个HttpClient类然后创建一个Builder内部类代码如下
- 给HttpClient创建私有的构造器初始化OkHttpClient和Retrofit代码如下
- 建立POST和GET方法代码如下
- 最后是正真去执行网络请求的方法代码如下
- 建一个接口把请求网络的结果回调
- 当然我们还要有传参的方法service中通用方法的封装下面是封装retrofit的service
1、URL的简单构成
URL(Uniform Resource Locator)定义:统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。URL构成一般是这样的:
[ scheme : ][ //authority ][ path ][ ? query ]
URL的例子:
http://www.cnblogs.com:8080/yourpath/mdeditor?stove=10&path=1&id=6
- scheme : http
- authority : www.cnblogs.com:8080
- path : /yourpath/mdeditor
- query : 在 ?后的部分为 stove=10&path=1&id=6
因为 authority又进一步可以划分为 host:port形式,其中 host:port 用冒号分割,冒号前的是host,冒号后是port,所以:
- host : www.cnblogs.com
- port : 8080
上面讲的都是URL中的参数,除了URL中的参数还有两个HTTP协议中常用的参数是:header (请求头)和body(常用于post请求中的请求提,有多种封装方法,不暴露在URL中)这两个参数。
综上:可以看出整个网络请求中的参数主要可以分为:scheme、authority、path、query、header、body 这6块。
2、Http中GET和POST的区别
HTTP定义了与服务器交互的不同方法,最基本的方法有四种,分别是GET、POST、PUT、DELETE。一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。
1、GET请求: GET是向服务器发起索取数据的一种请求,用于获取信息而非修改信息;GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以 ? 分割 URL 和 传输数据,参数之间以 & 相连,例如:
login?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5;
%XX中的XX为该符号以16进制表示的ASCII,如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
2、POST请求: POST是向服务器提交数据的一种请求,POST请求可能会导致新的资源的建立和/或已有资源的修改; POST把提交的数据则放置在是HTTP包的包体中。
3、Retrofit 概览
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。这里没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit 来完成的,因为Retrofit 2.0 开始内置 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,二者分工协作。
应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。
3.1、Url配置
Retrofit 支持的协议包括 GET/POST/PUT/DELETE/HEAD/PATCH,这些协议均以注解的形式进行配置,例如GET的用法:
网络请求地址: http://gank.io/api/data/福利/10/1
public interface GirlsService { @GET("api/data/{type}/{count}/{page}") Call<ResponseBody> getGirls(@Path("type") String type, @Path("count") int count, @Path("page") int page); }
这些注解都有一个参数value,用来配置其路径,比如示例中的”api/data/{type}/{count}/{page}”,@GET中所填写的value和baseUrl组成一个完整 的路径,baseUrl会在构造Retrofit对象时给出,@GET注解中使用{type}、{count}、{page}声明了访问路径,可以把{type}、{count}、{page}当做占位符,实际运行中会通过@PATH(“type”)所标注的参数进行替换。
3.2、Retrofit注解
Retrofit通过使用注解来简化请求,大体分为以下几类:
1. 用于标注请求方式的注解
2. 用于标记请求头的注解
3. 用于标记请求参数的注解
4. 用于标记请求和响应格式的注解
1、请求方法注解
2、请求头注解
3、请求参数注解
4、请求和响应格式注解
3.3、请求参数注解的使用方法
1、@Path:动态的url访问(这里只列举GET请求,其他请求一样处理)
网络请求地址: http://gank.io/api/data/Android/10/1
Path 分为带参数和不带参数两种情况:
不带参数的情况:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
带参数的情况:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
带参数就是在path路径中的type、count以及page是可以改变的,例如:http://gank.io/api/data/福利/10/1
1. 其中“福利”就是type,而且这个值还有其他选择,如Android、视频等;
2. 其中“10”就是count,表示每次请求的个数,可以随意替换;
3. 其中“1”就是page,代表页码,根据请求次数依次叠加;
2、@Query 和@QueryMap:查询参数的设置
网络请求地址 :https://api.github.com/users/whatever?client_id=xxxx&client_secret=yyyy
@Query的使用方法:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
访问代码:
- 1
- 1
这样我们就完成了参数的指定,当然相同的方式也适用于POST,只需要把注解修改为@POST即可。
但是在实际的应用场景中,我们每次网络请求很少会只有一个查询参数,这时候就需要能够携带多个Query的@QueryMap:
@QueryMap的使用方法:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
访问代码:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
3、@Body:以POST请求体的方式向服务器上传 json 字符串
网络请求地址 :https://api.github.com/login
应用程序跟服务器通信,很多时候会选择直接使用POST方式将 json 字符串作为请求体发送到服务器,使用方法如下:
先定义个实体类User:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
@Body的使用方法:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
访问代码:
- 1
- 1
其实就是使用@Body这个注解标识我们的参数对象即可。
但是我们平时并不会这样写,因为每次请求都要创建一个实体类,例如访问代码中的User,这样就会很麻烦,因为我们POST的是 json 字符串,所以我们可以这样写:
我们将@Body原来声明的参数User 改成了OKhttp中的RequestBody,并且使用 @Headers声明了请求头的参数类型:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
访问代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
首先创建一个JSONObject对象,然后将参数put到JSONObject对象中,再将这个JSONObject对象转换成我们需要的参数RequestBody ,然后就可以访问了。
4、@Field 和 @FieldMap:以表单的方式传递键值对 @FormUrlEncoded
网络请求地址 :https://api.github.com/login
其实我们用 POST 的场景相对较多,绝大多数的服务端接口都需要做加密、鉴权和校验,GET 显然不能很好的满足这个需求,使用 POST 提交表单的场景就更是刚需了;
定义一个方法:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
访问的代码:
- 1
- 1
首先通过@POST指明Url,并且添加@FormUrlEncoded,然后通过@Field 声明了表单的项。
如果你不确定表单项的个数,还有能够携带多个Field的FieldMap:
定义一个方法:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
访问的代码:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
5、@Part & 和 PartMap:文件上传 @Multipart
单文件上传方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
单文件上传访问代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
多文件上传访问代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
6、@Url :指定请求路径
网络请求地址: http://gank.io/api/data/Android/10/1
由于使用@Path 会存在 url 被转义的情况,但是使用 @Url 就不存在这个问题;
定义一个方法:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
访问的代码:
- 1
- 1
3.4、Converter.Factory 数据转换工厂
给Retrofit增加数据解析功能:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
这里添加了gson转化工厂:addConverterFactory(GsonConverterFactory.create() ),Retrofit内部会根据这个转换工厂及返回数据所指定的泛型实现直接转换;Retrofit也支持自带如下格式:
- Gson: com.squareup.retrofit2:converter-gson
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
4、使用Retrofit进行网络请求
我们以GET请求为例,实现一个完整的网络请求;
1、首先创建具体网络请求方法
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
注意:如果 Call 的泛型指定的类不是ResponseBody,Retrofit会将返回的 string 用 json 转换器自动转换该类的一个对象,转换不成功就报错;如果不需要Gson转换,那么就指定泛型为ResponseBody,而且只能是ResponseBody,子类都不行。
2、然后创建一个Retrofit实例
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等;还有addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值;client用于传入具体的OkHttpClient;
3、获取定义的接口实例
通过 Retrofit.create 就可以拿到我们定义的 ApiService 的实例,
- 1
- 1
4、调用请求方法,并得到 Call 实例
- 1
- 1
Call 其实在Retrofit中就是行使网络请求并处理返回值的类,调用的时候把需要拼接的参数传递进去,最后得到的完整Url地址为:
http://gank.io/api/data/福利/10/1
5、使用Call实例完成同步或异步请求
同步请求
- 1
- 1
需要注意的是同步网络请求一定要在子线程中完成,不能直接在UI线程执行,不然会crash;
异步请求
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
6、取消这个请求
Call 提供了cancel 方法可以取消请求,前提是该请求还没有执行;
- 1
- 1
5、Retrofit二次封装–Builder模式
4.1 Builder模式的定义
将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。
4.2 使用Builder的目的:
当我们请求网络的时候,需要一系列的参数,包括路径和请求参数,请求头,设置超时时间等等,考虑到以后可能会改,比如添加SSL判断,或者要使用gzip等,为了提高他的可拓展性,使用建造者模式可以让外部调用post方法以后,当内部逻辑改变时,不用去修改。直接在Builder类进行添加新的变量,并在post方法内部进行逻辑更改就好,外部使用者不会受到影响。
具体实现后的使用方法:
private void getData() { HttpClient client = new HttpClient.Builder() .baseUrl("https://10.33.31.200:8890/") .url("msp/mobile/login") .params("username", "admin") .params("password", "B213CEAC2F1022023EF2699AA62599CF") .params("passwordLevel", "0") .params("captcha", "") .params("captchaKey", "") .params("loginAddr", "10.33.31.200") .params("mac", "6c:5c:14:8a:72:12") .build(); client.get(new HttpClient.OnResultListener() { @Override public void onSuccess(String result) { Log.d("HttpClient", result); } @Override public void onFailure(String message) { Log.d("HttpClient", message); } });
4.3 具体的封装过程
先创建一个HttpClient类,然后创建一个Builder内部类,代码如下:
public static final class Builder { private String baseUrl = BASE_URL; private String url; private Map<String, String> params = new ArrayMap<>(); public Builder() { } public Builder baseUrl(String baseUrl) { this.baseUrl = baseUrl; return this; } public Builder url(String url) { this.url = url; return this; } public Builder params(String key, String value) { this.params.put(key, value); return this; } public HttpClient build() { BASE_URL = baseUrl; HttpClient client = HttpClient.getIns(); client.setBuilder(this); return client; }}
给HttpClient创建私有的构造器,初始化OkHttpClient和Retrofit,代码如下:
private HttpClient() { HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(MyApplication.getIntstance(), new int[0], R.raw.ivms8700, STORE_PASS); OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(7000, TimeUnit.MILLISECONDS) .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager) .hostnameVerifier(HttpsUtils.getHostnameVerifier()) .build(); retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .client(okHttpClient) .build(); }
建立POST和GET方法,代码如下:
public synchronized void get(final OnResultListener onResultListener) { Builder builder = mBuilder; if (!builder.params.isEmpty()) { String value = ""; for (Map.Entry<String, String> entry : builder.params.entrySet()) { String mapKey = entry.getKey(); String mapValue = entry.getValue(); String span = value.equals("") ? "" : "&"; String part = StringUtil.buffer(span, mapKey, "=", mapValue); value = StringUtil.buffer(value, part); } builder.url(StringUtil.buffer(builder.url, "?", value)); } mCall = retrofit.create(HttpParams.class).params(builder.url); request(builder, onResultListener);}public synchronized void post(final OnResultListener onResultListener) { Builder builder = mBuilder; mCall = retrofit.create(HttpParams.class) .params(builder.url, builder.params); request(builder, onResultListener);}
最后是正真去执行网络请求的方法,代码如下:
private void request(final Builder builder, final OnResultListener onResultListener) { mCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { Log.d("HttpClient", "Call<ResponseBody> Request onSuccess=====>" + call.request()); int code = response.code(); if (code == 200) { String result = null; try { result = response.body().string(); Log.d("HttpClient", "Http Response=====>" + result); } catch (IOException e) { e.printStackTrace(); } onResultListener.onSuccess(result); return; } onResultListener.onFailure(response.toString()); } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.d("HttpClient", "Call<ResponseBody> Request onFailure=====>" + call.request()); t.printStackTrace(); onResultListener.onFailure(t.getMessage()); } });}
建一个接口,把请求网络的结果回调:
public interface OnResultListener { void onSuccess(String result); void onFailure(String message);}
当然我们还要有传参的方法,service中通用方法的封装,下面是封装retrofit的service:
既然要封装,肯定就不能用retrofit的常规用法:ApiService接口里每个接口文档上的接口都写一个方法,而是应该用QueryMap/FieldMap注解,接受一个以Map形式封装好的键值对。
注意:如果方法的泛型指定的类不是ResonseBody,retrofit会将返回的string成用json转换器自动转换该类的一个对象,转换不成功就报错.
public interface HttpParams {/** * POST方式将json字符串作为请求体发送到服务器 * 其中@FormUrlEncoded 以表单的方式传递键值对 * 其中 @Path:所有在网址中的参数(URL的问号前面) * 另外@FieldMap 用于POST请求,提交多个表单数据,@Field:用于POST请求,提交单个数据 * 其中@Path(value = "filePath", encoded = true) String filePath是为了防止URL被转义为https://10.33.31.200:8890/msp%2Fmobile%2Flogin%3FloginAddr=10.33.31.200 */@FormUrlEncoded@POST("{dataPath}")Call<ResponseBody> params(@Path(value = "dataPath", encoded = true) String dataPath, @FieldMap Map<String, String> param);@GETCall<ResponseBody> params(@Url String url);@Multipart@POST("upload")Call<ResponseBody> upload(@Part("description") RequestBody description, @Part MultipartBody.Part file);}
这样一个retrofit的二次封装库算是做好了。
点击源码查看详细使用方法:HttpClient
- 使用Retrofit2封装适用于组件化项目的网络库
- 使用Retrofit2封装适用于组件化项目的网络库
- Rxjava2和Retrofit2网络库的封装
- Mvp+Retrofit2+RxJava2 项目中使用的简洁封装
- Mvp+Retrofit2+RxJava2 项目中使用的简洁封装
- 基于Retrofit2+OkHttp封装的Android网络操作库RetrofitClient
- Retrofit2+Rxjava2网络请求框架的封装
- Retrofit2网络框架的使用
- 使用retrofit2和rxjava封装网络框架RNet:(一)RNet的使用
- 使用retrofit2和rxjava封装的网络框架RNet:(二)RNet的源码解析
- 一个使用超简单C++封装的网络库(包含服务器端和客户端,跨平台,比较适用于移动终端)
- Android 网络框架 Retrofit2.0介绍、使用和封装
- RxJava2+Retrofit2网络请求框架封装及使用
- Android 网络框架 Retrofit2.0介绍、使用和封装
- retrofit2+okhttp3+rxjava网络封装
- 简单实现RxJava2+Okhttp+Retrofit2的网络请求框架封装
- Retrofit2网络框架的使用(一)
- RxJava + Retrofit2.0的项目实战完美封装
- Java基础语法
- SSH整合开发之配置文件
- SPI通信协议
- lintcode--加油站
- dataTable导入combobox下拉框
- 使用Retrofit2封装适用于组件化项目的网络库
- 数组冒泡排序代码
- 409. Longest Palindrome
- Listener监听器
- Java native 关键字
- MyEclipse启动:Could not create the view: An unexpected exception was thrown.
- 你需要理解的Java反射机制知识总结
- 初始Python
- SpringMVC的解释与搭建Maven私有代理服务器