前面我们学习过了Android网络开发中的Okhttp框架和Asynchttpclient框架,这一篇我们学习一个非常强大的框架——Retrofit框架。Retrofit现在最新版本是2.1.0,Retrofit框架是Square公司推出来的,是在Okhttp基础上的进一步封装。
在这里对Okhttp做一些说明:Okhttp是基于HTTP协议封装的,跟HttpClient、HttpUrlConnection的职责是一样的,虽然Okhttp也可以开线程,但它是更多偏向于真正的网络通信。Okhttp是基于NIO和OKio的,这里可能很多人不明白什么是IO、NIO和Okio,这里做一个简单的解释:IO和NIO是Java的概念,IO是一种阻塞式的数据读取,举个例子:我向网络中请求数据,程序就一种在等待,等到网络中返回数据并解析好了,程序才往下执行,这种就是阻塞式的;而NIO则是非阻塞式的,还是刚才那个例子,向网络请求数据,但是程序继续执行,等到数据返回和处理完才通知我,然后通过回调处理,这种就是非阻塞式的,非阻塞式的效率比阻塞式的高。而Okio是Square公司在IO和NIO基础上封装的一个更高效、更简单的数据处理库。
下面我们介绍一下Retrofit这个框架
概述
Retrofit框架是基于Okhttp封装的,在1.0版本的时候是默认采用Okhttp通信,不过在没有添加Okhttp依赖的时候也可以采用自带的网络请求通信,解析数据方式是Gson(一个Google推出的Json数据解析框架,后面我们会介绍),所以我们需要添加Gson的依赖。但是在2.0后的版本中,Retrofit是必须要采用Okhttp请求,还有请求返回数据的解析也不是Gson,需要我们自己手动指定。
Retrofit是通过注解将HTTP请求转化为Java接口,那么,我们了解一下Retrofit提供有哪些注解?
请求方式的注解
- DELETE:构建一个DELETE请求
- GET:构建一个GET请求
- HTTP:可以代替其它的任意一种
- OPTIONS:构建一个OPTIONS请求
- PATCH:构建一个PATCH请求
- POST:构建一个POST请求
- PUT:构建一个PUT请求
上面列出的都是Retrofit2.0提供的网络请求方式的注解,其中,我们可以通过HTTP这个注解自定义一个请求方式,例如:
interface Service {
@HTTP(method = "CUSTOM", path = "custom/endpoint/")
Call<ResponseBody> customEndpoint();
}
甚至HTTP注解也可以用于发送一个DELETE请求方式的请求体,例如:
interface Service {
@HTTP(method = "DELETE", path = "remove/", hasBody = true)
Call<ResponseBody> deleteObject(@Body RequestBody object);
}
标记注解
- FormUrlEncoded注解:与@Field或者@FieldMap一起使用,上传表单数据
- Multipart注解:与@Part一起使用
- Streaming注解:用于下载大文件
参数注解
- Body注解::用于POST请求体,将实例对象根据转换方式转换为对应的json字符串参数,这个转化方式是GsonConverterFactory定义的。
Field注解:@Field和@FormURLEncoded一起使用,表示提交一个表单数据给服务器,我们都知道,表单数据是以键值对的形式存在,所以,@Field注解中的参数字符串为“键”,而被@Field标注的参数值为 “值”,例如:
@FormUrlEncoded @POST("/list") Call<ResponseBody> example(@Field("name") String... names);
FieldMap注解:这个注解跟上面的@Field注解差不多,也是和@FieldURLEncoded一起使用。注解的是一个Map,键作为表单数据的”键”,值作为表单数据的”值”,例如
@FormUrlEncoded @POST("/things") Call<ResponseBody> things(@FieldMap Map<String, String> fields);
Part注解:与@PartMap一样
PartMap注解:用于POST文件上传,其中@Part MultipartBody.Part代表文件,@Part(“key”)RequestBody代表参数,需要添加@Multipart表示支持文件上传的表单,Content-Type: multipart/form-data,例如:
@Multipart @POST("/upload") Call<ResponseBody> upload( @Part("file") RequestBody file, @PartMap Map<String, RequestBody> params);
Query注解:表示查询参数,非常简单
- QueryMap注解:查询参数,用于GET查询,需要注意的是@QueryMap可以约定是否需要encode
其它注解
Path注解:URL占位符,我们看一个例子:
@GET("/image/{id}") Call<ResponseBody> example(@Path("id") int id); @GET("/user/{name}") Call<ResponseBody> encoded(@Path("name") String name); 就是将例子中用@Path标注的参数用来替换服务器地址中使用“{}”括起来的字符串,简单说就是,这是一个占位符。 但是注意的是,不能替换URL中的字符串,例如:在http://www.hitomis.com/user.do?action=findUser&username="zuck"中,不能替换action=findUser&username="zuck"路径中的内容
Url注解:使用全路径复写baseUrl,用于不是同一baseUrl的场景
Header注解:设置请求头,可以不设置。设置的话就是@Header里面注解参数为”键”,被@Header注解的参数作为”值”,例如:
@GET("/") Call<ResponseBody> foo(@Header("Accept-Language") String lang);
Headers注解:用于修饰方法,用于设置多个Header值
- HeaderMap注解:与Header类似
好了,Retrofit就简单介绍到这里,下面我们还是学习一下怎么使用这个框架!
使用
Android Studio使用需要在Gradle中添加依赖,建议先去GitHub上获取最新的依赖,在这里给出Retrofit的GitHub地址和官网地址:
GItHub地址:https://github.com/square/retrofit
官网地址:http://square.github.io/retrofit/
compile 'com.squareup.retrofit2:retrofit:2.1.0'
这是Gradle依赖,我们需要用到java/Json转换器工厂,所以需要添加转换器的依赖,可以直接在AS里面搜索最新,包括上面的Retrofit依赖也可以在AS里面搜索
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
如果你想用RxJava支持的适配器的话,需要添加适配器的依赖,也可以直接在AS里面搜索,这里我们没有用到,所以就不需要添加支持RxJava适配器的依赖。
简单描述一下使用Retrofit框架的基本步骤:
第一步当然就是添加依赖了,Android开发的话基本都是用Gradle的,如果是Java可以用Maven。
第二步则是定义一些接口,Retrofit是面向接口编程的。
第三步是创建Retrofit对象,一般是使用Builder模式创建这个对象
第四步就是使用Retrofit。
上面列出的就是使用Retrofit的基本步骤,下面我们具体使用一下这个强大的网络请求框架
我们说过Retrofit是面向接口编程的,所以使用之前我们需要定义一些我们用到的接口,那么我们先看一下我们定义的接口:
package com.example.frame.common;
import com.example.frame.bean.ContributorsBean;
import java.util.List;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
import retrofit2.http.Url;
/**
* Created by :Huawen
* Created time : 2016/9/22 16:15
* Description :
* Github: https://github.com/Devin1102
*/
public interface RetrofitAPI {
/**
* 普通的请求
*
* @return
*/
@GET("repos/square/retrofit/contributors")
Call<List<ContributorsBean>> getData();
/**
* 查询
*
* @param username
* @return
*/
@GET("sayHello")
Call<ResponseBody> getQuery(@Query("username") String username);
/**
* 上传文件
*
* @param fileUrl
* @return
*/
@GET
Call<ResponseBody> downloadFiel(@Url String fileUrl);
/**
* 上传文件
*
* @param description
* @param file
* @return
*/
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(@Part("description") ResponseBody description, @Part MultipartBody.Part file);
/**
* 上传表单数据
*
* @param username
* @param password
* @return
*/
@FormUrlEncoded
@POST("sayHello")
Call<ResponseBody> postForm(@Field("username") String username, @Field("password") String password);
}
除了接口之外,我们还将Retrofit对象的获取封装了,看一下封装的代码:
public class RetrofitService {
protected Retrofit getRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UrlUtils.GET_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
public RetrofitAPI createRetrofitAPI() {
return getRetrofit().create(RetrofitAPI.class);
}
}
这里也没有什么好解析,我们这里没有添加OkhttpClient端,Retrofit会自动创建一个,当然我们也可以自己手动设置一个。
使用Retrofit实现简单的GET请求
使用Retrofit框架完成GET请求非常简单,我们先看一下代码:
private void retrofitGet() {
Call<List<ContributorsBean>> call = mRetrofit.getData();
call.enqueue(new Callback<List<ContributorsBean>>() {
@Override
public void onResponse(Call<List<ContributorsBean>> call, Response<List<ContributorsBean>> response) {
Log.i(TAG, "onResponse请求成功");
List<ContributorsBean> beans = response.body();
for (ContributorsBean contributorsBen : beans) {
Log.i(TAG, "onResponse" + contributorsBen);
}
}
@Override
public void onFailure(Call<List<ContributorsBean>> call, Throwable t) {
Log.i(TAG, "onFailure请求失败");
}
});
}
这里对应的接口里面的GET请求,里面我们没有设置参数。这种请求跟Okhttp框架请求方式差不多,使用的也是异步的请求。
Retrofit实现Query参数请求
这个是一个简单的GET请求,不过设置一些参数,上面我们说过@Query和@QueryMap是用于GET请求的设置参数,我们看一下具体的代码:
private void retrofitGetQuery() {
Call<ResponseBody> call = mRetrofit.getQuery("Devin");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, getResources().getString(R.string.req_failed));
}
});
}
这里我们设置一个参数,与接口一致。
Retrofit实现POST上传表单数据
我们在做实际项目的时候,需要向服务器传递一些表单数据,比如登录、注册功能的时候,Retrofit也给我们提供了实现表单数据上传的方法,我们看一下具体的例子:
private void retrofitPostForm() {
Call<ResponseBody> call = mRetrofit.postForm("Devin", "Devin");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, getResources().getString(R.string.req_failed));
}
});
}
设置表单数据上传的时候需要在接口中设置@FormUrlEncoded这个方法标记,通过@Field或者@FieldMap实现设置数据。
Retrofit实现文件下载
文件下载是经常使用的网络操作,比如图片和视频下载、APP更新等。我们看一下Retrofit框架提供的文件下载功能:
private void retrofitDownloadFile() {
Call<ResponseBody> call = mRetrofit.downloadFiel("http://repo1.maven.org/maven2/com/squareup/retrofit2/retrofit/2.1.0/retrofit-2.1.0.jar");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
InputStream is = response.body().byteStream();
try {
FileOutputStream fos = new FileOutputStream(new File("/sdcard/retrofit-2.1.0.jar"));
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
}
});
}
在定义文件下载接口的时候,我们设置了@Url参数,这个是复写BaseUrl的,用于我们不使用baseUrl的情况,里面被@Url标志的数据是字符串类型,就是文件下载的URL。然后下载成功之后,以读流的形式将文件保存起来。
Retrofit实现文件上传
文件上传也是经常使用的功能,我们看一下Retrofit框架实现文件上传的功能:
private void retrofitUploadFile() {
String descriptionString = "UploadFile";
ResponseBody description = ResponseBody.create(MediaType.parse("multipart/form-data"), descriptionString);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), new File("/sdcard/retrofit-2.1.0.jar"));
MultipartBody.Part body = MultipartBody.Part.createFormData("picture", "retrofit-2.1.0.jar", requestFile);
Call<ResponseBody> call = mRetrofit.uploadFile(description, body);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
}
});
}
文件上传需要在接口中设置@Multipart标记,在接口中我们设置了两个参数:@Part(“description”) ResponseBody description, @Part MultipartBody.Part file,我们需要构建两个参数,然后实现上传。
以上是我对Retrofit框架的基本理解和具体的使用,可能写法不是很好,如果大家有更好的写法,欢迎大家留言交流。
上面Demo已经上传到GitHub了,当然还有其它框架的使用Demo,Demo的GitHub地址:https://github.com/Devin1102/AndroidFrameDemo