Retrofit是Square开发的一个用于网络请求的开源库,内部封装了okhttp,并且和RxAndroid完美的兼容,使得Android的开发效率增加不少的同时也使代码变得清晰易读。
本次的学习建立在上次okhttp学习的基础之上,service端的程序也是通过自己搭建并完成的。服务端的程序比较简单,本次的retrofit学习不对服务端的程序进行过多的讲解。如果有疑问,可以参考上次okhttp的相关内容。首先还是先列举出本次学习用到的资源。
- 官方教程
- github地址
- 李江东博客
- 鸿洋大神
- 推酷上的一篇文章
- 怪盗kidou
- 比较好的资料
搭建使用环境
下载最新的jar或者构建Maven仓库:
com.squareup.retrofit2
retrofit
2.2.0
或者直接在项目的build.gradle
中添加如下的依赖
compile 'com.squareup.retrofit2:retrofit:2.2.0'
ps,在添加依赖之前,最好先去github上看当前的最新版本。
在使用之前需要先添加网络访问权限。
使用示例
下面通过使用retrofit实现get、post、文件上传、下载来演示retrofit的使用。
get请求
retrofit在使用的过程中,需要定一个接口对象,我们将它命名为IUserService:
public interface IUserService {
@GET("rlogin")
Call loginByGet(@Query("user") String user, @Query("passwd") String passwd);
}
然后在需要MainActivity中构建Retrofit,并生成一个实现接口的方法。
retrofit = new Retrofit.Builder()
.baseUrl("http://172.18.9.31:8080/OkhttpTestService/")
.build();//构建一个retrofit 对象
IUserService repo = retrofit.create(IUserService.class);
Call call = repo.loginByGet("reoger","123456");//实例loginByGet对象
call.enqueue(new Callback() {//异步执行
@Override
public void onResponse(Call call, Response response) {
try {
mImageView.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call call, Throwable t) {
mImageView.setText("file to get");
}
});
简要的说明一下上后面的代码。首先,在IUserSevice接口中,通过@GET("rlogin")注释指定了路径,然后通过后面的loginByGet具体化了url。结合BaseUrl,实例化出来的Url实际上是:http://172.18.9.31:8080/OkhttpTestService/rlogin?user=reoger&passwd=123456,后面的user和passwd是在实例化时传入的。
可能这么讲会有点难懂,先看一个简单的例子。
注解中的参数为请求的相对URL路径@GET("users/list")
就相当与在BaseUrl后加上/users/list。在本例中就相当于:
http://172.18.9.31:8080/OkhttpTestService/users/list
当然,我们可以会遇到URL并不是固定的那种情况。这个时候我们就可以这么写:
@GET("group/{id}/users") //注意 字符串id
List groupList(@Path("id") int groupId); //注意 Path注解的参数要和前面的字符串一样 id
这个时候我们构造groupList时会传入id,而这个id的值会代替传入的groupId值代替。
{}用于表示是可以替换的区域,而函数参数必须用@Path注解声明。参见上例。
然后,当需要用我们的请求含有参数的时候,这个时候就需要使用@Query
注解来完成。
例如访问:http://baseurl/users?user=username
就需要:
@GET("users")
Call getUsersBySort(@Query("user") String sort);
点击之后,发现服务端能正确接收来自客服端的请求,并且客服端也能正确接收来自服务端的反馈信息。
这里有一点还需要提出来一下,Call
为了后面后面的代码比较简洁,我们直接在这里先将后面用到的retrofit和repo对象,已经实现方法executeByEn()统一声明。
retrofit = new Retrofit.Builder()
.baseUrl("http://172.18.9.31:8080/OkhttpTestService/")
.addConverterFactory(GsonConverterFactory.create())
.build();
repo = retrofit.create(IUserService.class);
private void executeByEn(Call call) {
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
try {
mImageView.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call call, Throwable t) {
mImageView.setText("file to get");
}
});
}
Post请求
还是先在IUserService中进行申明:
@POST("rlogin")
@FormUrlEncoded
Call loginByPost(@Field("user")String user,@Field("passwd") String passwd);
可以看到,这里我们使用Post作为注解,说明这是一个Post请求。添加FormUrlEncoded
,然后通过@Field
添加参数即可。
访问的代码:
Call call = repo.loginByPost("reoger","12346");
executeByEn(call);
此时,我们应该就能同get请求一样将数据传递到服务端,并接收来及服务端的消息。
Post向服务端传递String对象。
首先还是在IUserService中进行声明:
@POST("rpostString")
Call postString(@Body RequestBody user);
通过 @Body 注解可以声明一个对象作为请求体发送到服务器。
访问代码:
RequestBody body=RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),"Here is my server to pass the data, can be a string, but of course, can also be JSON data");
Call call = repo.postString(body);
executeByEn(call);
这里还是给出服务端的代码吧,服务端接收到数据后会给客户端返回一个收到的信息。
HttpServletRequest request = ServletActionContext.getRequest();
ServletInputStream is = request.getInputStream();
StringBuilder sb = new StringBuilder();
int len = 0;
byte[] buf = new byte[1024];
while((len=is.read(buf))!=-1){
sb.append(new String(buf,0,len));
}
System.out.println(sb.toString());
HttpServletResponse response = ServletActionContext.getResponse();
PrintWriter writer = response.getWriter();
writer.write("reces: "+sb.toString());
writer.flush();
return null;
访问之后,会发现我们的服务端正确接收到了我们发送的string数据,客户端也成功接收到了来自服务端的数据。
Post 上传Json格式数据
如果你只是单独想上传Json格式的数据到服务器,你完全可以写的更加简单。
@POST("rpostString")
Call postJson(@Body User user);
访问接口:
Call call = repo.postJson(new User("reoger","love"));
executeByEn(call);
是吧。两三行代码就解决了。那为什么我们传入User这样一个Bean对象传到服务端的时候就变成了Json数据呢。主要原因我我们在构造retrofit的时候添加的转换方法、
.addConverterFactory(GsonConverterFactory.create())
为此,我们还需要添加额外的依赖:
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
具体的转换过程就完成不需要我们来实现了。是不是很方便。
Post上传单个文件
还是先声明:
@Multipart
@POST("rpostSingerFile")
Call uploadSingerFile(@Part MultipartBody.Part mPhoto, @Part("user")RequestBody user,@Part("passwd") RequestBody passwd);
这里@MultiPart的意思就是允许多个@Part了,我们这里使用了3个@Part,第一个我们准备上传个文件,使用了MultipartBody.Part类型,其余两个均为简单的键值对。
使用:
File file = new File(Environment.getExternalStorageDirectory(),"test.jpg");
if(!file.exists()){
Log.e("TAG","file is not exit!");
return ;
}
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("mPhoto", "test.jpg", photoRequestBody);
Call call = repo.uploadSingerFile(photo, RequestBody.create(null, "abc"), RequestBody.create(null, "123"));
executeByEn(call);
代码形式和前面的Okhttp基本差不多。
Post上传多个文件:
这里可能和服务端实际接收的代码有关,我这里是用了一种简单的方法来接收多个文件。服务端的代码如下:
public List image; // 上传的文件
public List imageFileName; // 文件名称
public List imageContentType; // 文件类型
//上传图片
public String upLoadMulitFile() throws IOException{
ServletActionContext.getRequest().setCharacterEncoding("UTF-8");
System.out.println("开始接收文件");
String dir = ServletActionContext.getServletContext().getRealPath("files");
// 取得需要上传的文件数组
List files = image;
if (files != null && files.size() > 0) {
System.out.println("image ="+image.get(0).getName());
for (int i = 0; i < files.size(); i++) {
FileOutputStream fos = new FileOutputStream(dir + "\\" + imageFileName.get(i));
FileInputStream fis = new FileInputStream(files.get(i));
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fis.close();
fos.close();
}
}
return null;
}
客服端我们是这么写的。:
@Multipart
@POST("rpostMulitFile")
Call upload(@Part()List parts);
使用:
File file1 = new File(Environment.getExternalStorageDirectory(),"test.jpg");
File file2 = new File(Environment.getExternalStorageDirectory(),"test.JPEG");
RequestBody photoRequestBody1 = RequestBody.create(MediaType.parse("application/octet-stream"), file1);
RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file2);
MultipartBody.Part photo1 = MultipartBody.Part.createFormData("image", "test22.jpg", photoRequestBody1);
MultipartBody.Part photo2 = MultipartBody.Part.createFormData("image", "test33.jpg", photoRequestBody2);
List parts = new ArrayList<>();
parts.add(photo1);
parts.add(photo2);
Call call = repo.upload(parts);
executeByEn(call);
其实还有很多可优化的余地,只是当作demo,学习,就并没有使用跟好的写法。比如鸿洋大神是这么干的。
public interface IUserBiz
{
@Multipart
@POST("register")
Call registerUser(@PartMap Map params, @Part("password") RequestBody password);
}
执行的代码:
File file = new File(Environment.getExternalStorageDirectory(), "messenger_01.png");
RequestBody photo = RequestBody.create(MediaType.parse("image/png", file);
Map photos = new HashMap<>();
photos.put("photos\"; filename=\"icon.png", photo);
photos.put("username", RequestBody.create(null, "abc"));
Call call = userBiz.registerUser(photos, RequestBody.create(null, "123"));
但是我并没有将其实现,可能是我服务端的代码限制了吧 。
下载
还是在IUserService中添加声明:
@GET("files/test.jpg")
Call download();
执行:
Call call = repo.download();
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
//save file in here
Log.d("TAG","downFile...");
InputStream is = response.body().byteStream();
int len;
try {
File file = new File(Environment.getExternalStorageDirectory(),"download.jpg");
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[128];
while( (len=is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
Log.d("TAG","down success!");
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
也比较简单吧。不过对象OkHttp好像也没有特别大的优势。
OkHttp的写法是这样的:
Request.Builder builder = new Request.Builder();
Request request = builder.get().url(BASE_URL+"files/test2.jpg").build();
okhttp3.Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
Log.e("TAG","Error"+e);
}
@Override
public void onResponse(okhttp3.Call call, Response response) throws IOException {
//保存到本地,
downSavetoFile(response);
}
});
当然,这些都只是一些比较简单的用法,也算是比较核心的用法了。接下来我们要学的可能就是细节上的设置和一些优化、封装等等了。暂时先告一段落吧~。明天看看还能能不能更一篇。
最后,有需要源代码的,可以戳这里。
五一快乐~~~。