标签:
上一篇文章博主介绍了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(); }
编写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就会被自动忽略
<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>
就是一个按钮,点击请求网络
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博文中使用的服务器,博文结束会分享
先使用浏览器测试一下返回的结果,先有一个直观的了解
第一次使用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); }
xml文件中添加要给按钮,这里不再贴出,按钮调用注册的方法
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:
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数据被成功的转化
首先是单文件上传,并且提交两个普通的键值对
/** * 测试提交一个文件和两个普通的键值对 * * @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);
123这部分是POST请求中最上面的,我们看到倒数第四行中发现了我们所写的内容,原来是为了既能输出name的value值,也能输出这分布的文件的名称,这也就解释的通上述第一个参数中的注解里面的内容了
还有另外两个键值对,也看一下报文吧
乱码的那几个字其实就是我提交的name的值 "小金子"
下面还有一个123是我提交的pass的值,而我们可以看到我们在请求接口中的@Part注解内的内容作为了这里的name属性的value值
上述查看报文完全是为了帮助读者了解为什么文件部分的那个RequestBody参数的注解@Part为什么是
file\"; filename=\"avatar.db
完全是为了在报文中输出文件名,这里我觉得官方网站还是可以再优化一下的,毕竟这样子让人不太好理解
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("test/uploadFiles") public Call<String> postFiles(@Body MultipartBody multipartBody );
/** * 多文件上传 * * @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循环来添加文件部分的请求体
标签:
原文地址:http://blog.csdn.net/u011692041/article/details/51626265