retrofit是okhttp的加强版,底层封装了Okhttp。准确来说,Retrofit是一个RESTful的http网络请求框架的封装。因为网络请求工作本质上是由okhttp来完成,而Retrofit负责网络请求接口的封装。
超级解耦
支持同步、异步、Rxjava
可以配置不同反序列化工具类来解析不同的数据,如json、xml
请求速度快,使用方便灵活简洁
添加依赖:
//Retrofit:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
//glide4: //用于图片处理
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
//RxJava:
implementation "io.reactivex.rxjava2:rxjava:2.1.3" // 必要rxjava2依赖
implementation "io.reactivex.rxjava2:rxandroid:2.0.1" // 必要rxandrroid依赖,切线程时需要用到
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' // 必要依赖,和Retrofit结合必须用到,下面会提到
/**
* 日志拦截器
*/
implementation 'com.squareup.okhttp3:logging-interceptor:3.3.1'
请求方法:
注解 | 说明 |
---|---|
@GET | get请求 |
@POST | post请求 |
@PUT | put请求 |
@DELETE | delete请求 |
@PATCH | patch请求 |
@HEAD | head请求 : 用于更新局部资源 |
@OPTIONS | options请求 |
@HTTP | put请求通过注解,可以替换以上所有的注解,它拥有三个属性:method、path、hasBody |
本篇只给大家讲解常用的请求方法@GET,@POST和@HTTP请求.
请求头:
请求头注解 | 说明 |
---|---|
@Headers | 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在 |
@Header | 作为方法的参数传入,用于添加不固定的header,它会更新已有请求头 |
本篇不给大家讲解请求头,请求头可以通过代码来写入,因为太多注解容易搞混,这里也不建议大家使用注解的方法添加请求头.
请求参数:
请求参数 | 说明 |
---|---|
@Body | 多用于POST请求发送非表达数据,根据转换方式将实例对象转化为对应字符串传递参数,比如使用Post发送Json数据,添加GsonConverterFactory则是将body转化为json字符串进行传递 |
@Filed | 用于POST请求 需要结合@FromUrlCoded以表单形式传递参数 |
@FiledMap | 用于POST请求中的表单字段,传递多个参数,需要结合@FromUrlEncoded使用 |
@Part | 用于POST请求,与@multipart配合使用,多用于上传单个文件 |
@PartMap | 用于POST请求,与@multipart配合使用,多用于上传多个文件 |
@Path | Url中的占位符 |
@Query | 用于Get请求中的参数 |
@QueryMap | 与Query类似,用于不确定表单参数 |
@Url | 指定请求路径 |
请求和响应格式:
请求和响应格式(标记)注解 | 说明 |
---|---|
@FromUrlEncoded | @FiledMap 和 @Filed 使用的时候必须添加 |
@Multipart | @PartMap 和 @Part 使用的时候必须添加 |
@Streaming | 表示响应用字节流的形式返回,如果没有使用注解,默认会把数据全部载入到内存中,该注解在下载大文件时特别有用 |
先定义一个接口:
public interface ApiServer {
//定义要请求的接口前半段,注意:这里必须以/结尾!!!
public static String BaseUri = "https://gank.io/api/data/%E7%A6%8F%E5%88%A9/";
/**
* 通过调用getMeiNvBen()使用@Path注解,将传过来的参数设置给@GET()
* 实现GET的传递
*/
@GET("{number}/{index}")
Observable<MeiNvBean> getMeiNvBean(@Path("number")String number, @Path("index")String index);
/**
*
* @param method 网络请求的方法(区分大小写)
* @param path 网络请求地址路径
* @param hasBody 是否有请求体
* @return
*/
@HTTP(method = "GET",path = "{number}/{index}" ,hasBody = false)
Observable<MeiNvBean> getHttpMeiNvBean(@Path("number")String number, @Path("index")String index);
}
我的MeiNvBean类是通过Postman软件来请求数据,然后通过GsonFromat来得到对应的实体类:
获取MeiNvBean 实体类:
package demo.ht.com.volley.bean;
import java.util.List;
public class MeiNvBean {
/**
* error : false
* results : [{"_id":"5be14edb9d21223dd50660f8","createdAt":"2018-11-06T08:20:43.656Z","desc":"2018-11-06","publishedAt":"2018-11-06T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/8733844e0b954e7e8e29102cefa32dbf","used":true,"who":"lijinshanmx"},{"_id":"5bcd71979d21220315c663fc","createdAt":"2018-10-22T06:43:35.440Z","desc":"2018-10-22","publishedAt":"2018-10-22T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/0bc3ed0deda74674b45bebb140bb5928","used":true,"who":"lijinshanmx"},{"_id":"5bc434ac9d212279160c4c9e","createdAt":"2018-10-15T06:33:16.497Z","desc":"2018-10-15","publishedAt":"2018-10-15T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/1635f0ffd94f40ff8058c2c4ff652e61","used":true,"who":"lijinshanmx"},{"_id":"5bbb0de09d21226111b86f1c","createdAt":"2018-10-08T07:57:20.978Z","desc":"2018-10-08","publishedAt":"2018-10-08T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/e930b75f48ae40b0a23f827e619d76a3","used":true,"who":"lijinshanmx"},{"_id":"5ba206ec9d2122610aba3440","createdAt":"2018-09-19T08:21:00.295Z","desc":"2018-09-19","publishedAt":"2018-09-19T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/d19c8feacc884e18898034c79e6e1a9d","used":true,"who":"lijinshanmx"},{"_id":"5b9771a29d212206c1b383d0","createdAt":"2018-09-11T07:41:22.491Z","desc":"2018-09-11","publishedAt":"2018-09-11T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/38ddf2ca672345889343b5748ffce78d","used":true,"who":"lijinshanmx"},{"_id":"5b830bba9d2122031f86ee51","createdAt":"2018-08-27T04:21:14.703Z","desc":"2018-08-27","publishedAt":"2018-08-28T00:00:00.0Z","source":"web","type":"福利","url":"https://img.lijinshan.site/images/a8aa7df22298413e9ca37c7a634e3425","used":true,"who":"lijinshanmx"},{"_id":"5b7b836c9d212201e982de6e","createdAt":"2018-08-21T11:13:48.989Z","desc":"2018-08-21","publishedAt":"2018-08-21T00:00:00.0Z","source":"web","type":"福利","url":"https://ws1.sinaimg.cn/large/0065oQSqly1fuh5fsvlqcj30sg10onjk.jpg","used":true,"who":"lijinshanmx"}]
*/
private boolean error;
private List<ResultsBean> results;
@Override
public String toString() {
return "MeiNvBean{" +
"error=" + error +
", results=" + results +
'}';
}
public boolean isError() {
return error;
}
public void setError(boolean error) {
this.error = error;
}
public List<ResultsBean> getResults() {
return results;
}
public void setResults(List<ResultsBean> results) {
this.results = results;
}
public static class ResultsBean {
/**
* _id : 5be14edb9d21223dd50660f8
* createdAt : 2018-11-06T08:20:43.656Z
* desc : 2018-11-06
* publishedAt : 2018-11-06T00:00:00.0Z
* source : web
* type : 福利
* url : https://img.lijinshan.site/images/8733844e0b954e7e8e29102cefa32dbf
* used : true
* who : lijinshanmx
*/
private String _id;
private String createdAt;
private String desc;
private String publishedAt;
private String source;
private String type;
private String url;
private boolean used;
private String who;
@Override
public String toString() {
return "ResultsBean{" +
"_id='" + _id + '\'' +
", createdAt='" + createdAt + '\'' +
", desc='" + desc + '\'' +
", publishedAt='" + publishedAt + '\'' +
", source='" + source + '\'' +
", type='" + type + '\'' +
", url='" + url + '\'' +
", used=" + used +
", who='" + who + '\'' +
'}';
}
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getPublishedAt() {
return publishedAt;
}
public void setPublishedAt(String publishedAt) {
this.publishedAt = publishedAt;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public boolean isUsed() {
return used;
}
public void setUsed(boolean used) {
this.used = used;
}
public String getWho() {
return who;
}
public void setWho(String who) {
this.who = who;
}
}
}
使用最原始未封装GET请求数据:
Retrofit apiServer = new Retrofit.Builder().
addCallAdapterFactory(RxJava2CallAdapterFactory.create()).
addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiServer.BaseUri)
.build();
// apiServer.create(ApiServer.class).getHttpMeiNvBean("8","1")
apiServer.create(ApiServer.class).getMeiNvBean("8", "1")
.subscribeOn(Schedulers.io())//IO线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MeiNvBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(MeiNvBean meiNvBean) {
mTv.setText(meiNvBean.toString());
Log.i("szjonNex", meiNvBean.toString());
}
@Override
public void onError(Throwable e) {
mTv.setText("请求错误:" + e.getMessage());
Log.i("onError", e.getMessage());
}
@Override
public void onComplete() {
}
});
还是先定义接口来实现请求接口的声明:
public interface ApiServer {
//定义要请求的接口前半段,注意:这里必须以/结尾!!!
public static String GuoChuangYun_BaseUri = "http://hn216.api.yesapi.cn/";
/**
*
* @param FormUrlEncoded 通过FieldMap上传必须加@FormUrlEncoded注解 表示声明表单上传
* @return
*/
@FormUrlEncoded
@POST("?s=App.User.Search")
Observable<GuoChuangYunBean> getPOSTGuoCHuangYunBean(@FieldMap Map<String ,String > map);
/**
*
* @param body POST请求体上传 FormBody app_key = new FormBody.Builder()
* .add("key", "valye")
* .build();
* @return
*/
@POST("?s=App.User.Search")
Observable<GuoChuangYunBean> getPOSTBodyGuoCHuangYunBean(@Body RequestBody body);
}
通过Postman请求POST接口,获取参数,在通过GsonFromat获取实体类;
获取GuoChuangYunBean实体类:
package demo.ht.com.volley.bean;
import java.util.List;
public class GuoChuangYunBean {
/**
* ret : 200
* data : {"err_code":0,"err_msg":"","users":[]}
* msg : V3.1.0 YesApi App.User.Search
*/
private int ret;
private DataBean data;
private String msg;
@Override
public String toString() {
return "GuoChuangYunBean{" +
"ret=" + ret +
", data=" + data +
", msg='" + msg + '\'' +
'}';
}
public int getRet() {
return ret;
}
public void setRet(int ret) {
this.ret = ret;
}
public DataBean getData() {
return data;
}
public void setData(DataBean data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public static class DataBean {
/**
* err_code : 0
* err_msg :
* users : []
*/
private int err_code;
private String err_msg;
private List<?> users;
@Override
public String toString() {
return "DataBean{" +
"err_code=" + err_code +
", err_msg='" + err_msg + '\'' +
", users=" + users +
'}';
}
public int getErr_code() {
return err_code;
}
public void setErr_code(int err_code) {
this.err_code = err_code;
}
public String getErr_msg() {
return err_msg;
}
public void setErr_msg(String err_msg) {
this.err_msg = err_msg;
}
public List<?> getUsers() {
return users;
}
public void setUsers(List<?> users) {
this.users = users;
}
}
}
使用最原始的请求方式请求POST数据:
HashMap<String, String> stringHashMap = new HashMap<>();
stringHashMap.put("app_key", "74D2E724FE2B69EF7EA3F38E9400CF71");
FormBody app_key = new FormBody.Builder()
.add("app_key", "74D2E724FE2B69EF7EA3F38E9400CF71")
.build();
Retrofit apiServer = new Retrofit.Builder().
addCallAdapterFactory(RxJava2CallAdapterFactory.create()).
addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiServer.GuoChuangYun_BaseUri)
.build();
apiServer.create(ApiServer.class)
.getPOSTBodyGuoCHuangYunBean(app_key)//@Body注解上传
// .getPOSTGuoCHuangYunBean(stringHashMap)//@FormUrlEncoded配合@FieldMap
.subscribeOn(Schedulers.io())//IO线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<GuoChuangYunBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(GuoChuangYunBean guoChuangYunBean) {
Log.i("szjPOSTonNext","\n"+guoChuangYunBean.toString());
mTv.setText("POST接口:\n" + guoChuangYunBean.toString());
}
@Override
public void onError(Throwable e) {
mTv.setText("POST接口请求错误:\n" + e.getMessage());
}
@Override
public void onComplete() {
}
});
来康康Log吧:
打开本地相册 注意申请动态权限
//打开本地相册
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/png");
intent.setAction(Intent.ACTION_PICK);
startActivityForResult(intent, REQUEST_PICTURE_CHOOSE);
重写onActivityResult()方法,获取选取的图片路径,并上传图片
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_PICTURE_CHOOSE) {
/**
* 先获取系统返回的图片路径
* 然后将图片上传,因为这里接口有问题,所以上传不成功!
*/
Uri selectedImage = data.getData(); //获取系统返回的照片的Uri
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);//从系统表中查询指定Uri对应的照片
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String path = cursor.getString(columnIndex); //获取照片路径
Log.i("szjpath", "\t\t" + path);
//上传图片
initUpdata(path);
}
}
定义接口,设置请求参数:
public interface ApiServer {
public static String GuoChuangYun_BaseUri = "http://hn216.api.yesapi.cn/";
/**
*
* @param body 上传图片
* @return
*/
// @Multipart 注意 : @Multipart 是配合@Part或@PartMap使用的,单独不能使用!!!!!!!
@POST("?s=App.CDN.UploadImg")
Observable<ImageBean> getImageBean( @Body RequestBody file);
/**\
*
* @param body 上传的app_key
* @param file 上传的单张图片
* @return
*/
@Multipart
@POST("?s=App.CDN.UploadImg")
Observable<ImageBean> uploadFile(@Part("app_key") RequestBody body, @Part MultipartBody.Part file);
}
通过Postman获取数据,并通过GsonFragment获取实体类,这里就不重复写了,和上面步骤一样:
File file = new File(path);
//方法一:
RequestBody requestBody = new MultipartBody.Builder().
setType(MultipartBody.FORM)
.addFormDataPart("app_key", "74D2E724FE2B69EF7EA3F38E9400CF71")
.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("image/png"), file))
.build();
//方法二:
RequestBody fileRQ = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), fileRQ);
RequestBody fb =RequestBody.create(MediaType.parse("text/plain"), "74D2E724FE2B69EF7EA3F38E9400CF71");
Retrofit apiServer = new Retrofit.Builder().
addCallAdapterFactory(RxJava2CallAdapterFactory.create()).
addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiServer.GuoChuangYun_BaseUri)
.build();
apiServer.create(ApiServer.class)
// .getImageBean(requestBody) //@Body 图片+参数 同时上传
.uploadFile(requestBody,part) //@Multipart 配合 @Part 单张图片 和单个参数上传
.subscribeOn(Schedulers.io())//IO线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ImageBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ImageBean imageBean) {
String url = imageBean.getData().getUrl();
Glide.with(RetrofitActivity.this)
.load(url)
.apply(new RequestOptions().placeholder(R.mipmap.ic_launcher))
.into(mImage);
mTv.setText("图片上传成功:\n" + imageBean.toString());
Log.i("szjOnNext", imageBean.toString());
}
@Override
public void onError(Throwable e) {
Log.i("onError", e.getMessage());
mTv.setText("图片上传失败:\n" + e.getMessage());
}
@Override
public void onComplete() {
}
});
我打开相册上传一张我健身的照片,来康康效果吧:
哈哈哈哈哈,献丑了献丑了,还怪不好意思的,羞羞QvQ
这里拿get接口举例:
public interface ApiServer {
@GET
Observable<MeiNvBean> getUrlMeiNvBean(@Url String url);
}
@Url特别简单,就是传一个完整的接口,通常用于GET请求,没什么难度,就不详细讲解了
Retrofit apiServer = new Retrofit.Builder().
addCallAdapterFactory(RxJava2CallAdapterFactory.create()).
addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiServer.BaseUri)
.build();
apiServer.create(ApiServer.class).getUrlMeiNvBean(ApiServer.BaseUri+"8/1")
.subscribeOn(Schedulers.io())//IO线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MeiNvBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(MeiNvBean meiNvBean) {
mTv.setText(meiNvBean.toString());
Log.i("szjonNex", meiNvBean.toString());
}
@Override
public void onError(Throwable e) {
mTv.setText("请求错误:" + e.getMessage());
Log.i("onError", e.getMessage());
}
@Override
public void onComplete() {
}
});
康康所有的效果吧:
本篇重复代码比较多,下一篇给大家吧Retrofit封装起来,有想看的同学可以点击查看
Git链接: langyangyang.
写一篇博客实在是太累了,而且还容易写错误导大家,如果有大佬看到有错误的地方,一定请在评论区留言,谢谢大家观看~