前言
上一篇文章博主介绍了okhttp这个同步的网络请求框架的使用,也说了okhttp的优点,但是同时也指出了okhttp在使用方面的不便,所以本文介绍基于okhttp的网络框架retrofit的使用,还是同一个公司的产品.
1.支持异步请求
2.用户自己定义请求接口,由框架自动通过代理实现,这就相当于把网络请求的部分代码抽取出来,代码更加简洁
3.利用注解描述请求的参数,极大的方便了用户的使用
本文重在讲解如何使用retrofit,原理将会在后续的博客中发表,请感兴趣的人关注一下
一个牵扯到网络请求的app,在很多的Activity或者Fragment中都可能会用到网络请求,所以这里就使用在项目中比较常见的搭建方式
声明请求接口
/** * Created by cxj on 2016/6/10. * 所有的网络请求 */ public interface NetWorkService { /** * 一个get请求的请求接口,返回是字符串 * * @return * */ @GET("https://publicobject.com/helloworld.txt") public Call<String> get(); }
暂时先书写一个返回字符串的请求接口,由于这里最后的结果需要转化成字符串,在框架中需要添加转化的组建,在官网中提供了以下几种
最后一个有转化成String的功能,所以这里我们要用到它,添加对应的依赖
compile ‘com.squareup.retrofit2:converter-scalars:2.0.0‘
这个依赖我是查询得到的,博主暂时也不知道哪里有完整的信息
快速搭建环境
编写MyApp继承Application,别忘了在项目清单文件中的application节点中添加name属性,并且添加网络请求的权限
/** * Created by cxj on 2016/6/10. */ public class MyApp extends Application { /** * 声明请求的接口 */ public static NetWorkService netWorkService; /** * 网络请求框架 */ public static Retrofit retrofit; @Override public void onCreate() { super.onCreate(); retrofit = new Retrofit.Builder() .baseUrl("http://192.168.1.102:8080/Retrofit/") .addConverterFactory(ScalarsConverterFactory.create()) .build(); //让框架自动实现我们的请求接口,让我们的请求接口可以被调用 netWorkService = retrofit.create(NetWorkService.class); } }
上述代码中其实就是创建了初始化了请求的接口,并且添加了一个转化器,这样子在任何地方就可以直接使用了
并且可以看到我们有一个baseUrl,这是相当于前缀的意思,如果你请求的地址不是绝对的,那么他会添加上这个前缀,如果你的请求是绝对的地址,那么这个baseUrl就会被自动忽略
Get请求
xml布局文件
<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <Button android:layout_width="match_parent" android:text="get请求测试<span style="font-family: Arial, Helvetica, sans-serif;">"</span> android:onClick="getTest" android:layout_height="wrap_content" /> </RelativeLayout></span>
就是一个按钮,点击请求网络
Activity中代码
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void getTest(View view) { Call<String> call = MyApp.netWorkService.get(); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { String result = response.body(); System.out.println("result = " + result); } @Override public void onFailure(Call<String> call, Throwable t) { System.out.println("请求失败"); } }); } }
相比我们之前使用okhttp来请求,是不是变得简单了许多呢?
来看看请求的结果,可以看到请求成功
Post请求提交普通键值对
这里就需要用到自己的服务器啦,这里还是使用okhttp博文中使用的服务器,博文结束会分享
先使用浏览器测试一下返回的结果,先有一个直观的了解
第一次使用name:cxj pass:123 来注册
第二次使用name:cxj pass:123 注册
可以看到返回的数据中有失败的提示和响应的错误信息,表示用户名已经存在,那么接口这边没有问题啦,那我们就开工写客户端的代码吧!
在网络请求接口中添加测试方法
public interface NetWorkService { /** * 一个get请求的请求接口,返回是字符串 * * @return */ @GET("https://publicobject.com/helloworld.txt") public Call<String> get(); /** * post提交数据 * * @param name * @param pass * @return */ @FormUrlEncoded @POST("test/register") public Call<String> register(@Field("name") String name, @Field("pass") String pass); }
可以看淡添加了一个注册的方法,使用POST请求,路径是相对路径,所以会拼接上前缀,提交的数据有name和pass,需要用@Field注解标明
xml文件中添加要给按钮,这里不再贴出,按钮调用注册的方法
Activity中注册方法代码
public void register() { Call<String> call = MyApp.netWorkService.register("cxj", "123"); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { //拿到返回的json数据 String result = response.body(); //打印结果 System.out.println("result = " + result); //利用Gson转化json为实体对象 Gson gson = new GsonBuilder().create(); //传化后的实体对象 Msg msg = gson.fromJson(result, Msg.class); //提示实体对象中的信息 Toast.makeText(MainActivity.this, msg.getMsgText(), Toast.LENGTH_LONG).show(); } @Override public void onFailure(Call<String> call, Throwable t) { System.out.println("注册失败"); } }); }
如果请求成功,就会提示msg对象中的信息,这里牵扯到两个实体对象,这里贴一下代码
Msg:
public class Msg { public static final String OK = "ok"; public static final String ERROR = "error"; /** * 只有两个值<br> * 1."ok"<br> * 2."error" */ private String msg = ERROR; /** * 信息的文本 */ private String msgText = ERROR; private Object data; public Msg() { super(); } public Msg(String msg, Object data) { super(); this.msg = msg; this.data = data; } public Msg(String msg, String msgText, Object data) { super(); this.msg = msg; this.msgText = msgText; this.data = data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public String getMsgText() { return msgText; } public void setMsgText(String msgText) { this.msgText = msgText; } @Override public String toString() { return "Msg [msg=" + msg + ", msgText=" + msgText + ", data=" + data + "]"; } }
User:
/** * 用户 * * @author cxj * */ public class User { private String name; private String pass; public User(String name, String pass) { this.name = name; this.pass = pass; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } /** * 只有当密码和名字一样的时候才相等 */ @Override public boolean equals(Object obj) { if (obj instanceof User) { User u = (User) obj; if (name != null && pass != null) { return name.equals(u.name); } } return false; } }
博主使用同一个用户名和密码连续注册两次的效果如下
效果图
可以看到第一次是提示成功,第二次是提示用户名存在,并且在控制台打印返回的结果可以看到是json数据,而我们提示成功说明了json数据被成功的转化
Post提交文件
首先是单文件上传,并且提交两个普通的键值对
先添加请求的接口中的上传单个文件的方法
/** * 测试提交一个文件和两个普通的键值对 * * @param fileBody * @param nameBody * @param passBody * @return */ @Multipart @POST("test/uploadFile") public Call<String> postFile(@Part("file\"; filename=\"avatar.db") RequestBody fileBody, @Part("name") RequestBody nameBody, @Part("pass") RequestBody passBody);
比上述两个稍微复杂一点,因为文件上传必须使用POST请求,而且是一个请求体中分多个部分,所以这里使用@Part表示一部分的请求体,第一个是我们的文件部分的数据,@Part里面的字符串当作这部分的名称,但是我们可以看到我们这里写的比较奇怪,那就先看一下这个请求提交的时候拦截下来的POST报文
123这部分是POST请求中最上面的,我们看到倒数第四行中发现了我们所写的内容,原来是为了既能输出name的value值,也能输出这分布的文件的名称,这也就解释的通上述第一个参数中的注解里面的内容了
还有另外两个键值对,也看一下报文吧
乱码的那几个字其实就是我提交的name的值 "小金子"
下面还有一个123是我提交的pass的值,而我们可以看到我们在请求接口中的@Part注解内的内容作为了这里的name属性的value值
上述查看报文完全是为了帮助读者了解为什么文件部分的那个RequestBody参数的注解@Part为什么是
file\"; filename=\"avatar.db
完全是为了在报文中输出文件名,这里我觉得官方网站还是可以再优化一下的,毕竟这样子让人不太好理解
Activity中上传文件的代码
public void postFile(View v) { //需要上传的文件 File f = new File(Environment.getExternalStorageDirectory(), "address.db"); //创建文件部分的请求体对象 RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), f); //普通键值对的请求体 RequestBody nameBody = RequestBody.create(null,"小金子"); RequestBody passBody = RequestBody.create(null,"123"); Call<String> call = MyApp.netWorkService.postFile(fileBody, nameBody, passBody); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { System.out.println("上传成功" + response.body() + response.code() + response.errorBody()); } @Override public void onFailure(Call<String> call, Throwable t) { System.out.println("上传文件失败"); } }); }
查看提交的效果
可以看到在服务器上已经收到啦
Post提交多文件
这回我们换一种方法来声明请求接口中的方法
@POST("test/uploadFiles") public Call<String> postFiles(@Body MultipartBody multipartBody );
这回直接让请求体作为参数,其实这个和自己利用okhttp发送已经很像了
Activity中上传多文件的代码
/** * 多文件上传 * * @param view */ public void postFiles(View view) { //需要上传的文件 File f1 = new File(Environment.getExternalStorageDirectory(), "address.db"); File f2 = new File(Environment.getExternalStorageDirectory(), "1.apk"); //创建文件部分的请求体对象 RequestBody fileBody1 = RequestBody.create(MediaType.parse("application/octet-stream"), f1); RequestBody fileBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), f2); MultipartBody multipartBody = new MultipartBody.Builder() .addFormDataPart("files", f1.getName(), fileBody1) .addFormDataPart("files", f2.getName(), fileBody2) .addFormDataPart("name", "小金子") .addFormDataPart("pass", "123") .build(); Call<String> call = MyApp.netWorkService.postFiles(multipartBody); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { System.out.println("上传成功"); } @Override public void onFailure(Call<String> call, Throwable t) { System.out.println("上传多文件失败"); } }); }
由于博主使用的是模拟器,没有那么多的文件测试,这里就用了两个文件,和n个文件原理是一样的,你们可以适度的封装一下,利用for循环来添加文件部分的请求体
服务器上的效果
提交成功啦
总结
其实在我个人看来retrofit只是对okhttp很好的进行了封装,让我们用起来得心应手,但是我强烈建议,在这个框架会使用的同时,也要适当的学习okhttp,对你是很有帮助的,如果对okhttp感兴趣的,请移步我的另一个篇博客
http://blog.csdn.net/u011692041/article/details/51620388
demo和服务器的测试项目代码下载地址
demo:https://github.com/xiaojinzi123/android-demos/tree/master/RetrofitDemo
服务器的:http://download.csdn.net/detail/u011692041/9545805
备用地址
https://yunpan.cn/cRQJyDihiXaWq
访问密码 009d