Android网络请求框架之Retrofit

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Android网络请求框架之Retrofit Retrofit之前言
Retrofit是square家族开源系列的精品之一,是一个简单好用的网络请求框架,基于REST 的架构风格,本框架的主要开发人Jake Wharton想必大家也有所耳闻,他也是REST的忠实拥护者和践行者。

引入开源框架,大部分开发者的目的是为了更快速的开发应用,省掉一些复杂的逻辑处理,Retrofit封装了网络请求,让开发者面向接口去请求服务,使用注解和代理去发起真正的请求,让开发者省掉所有网络请求的代码。

你不用担心android6.0不支持httpclient方式的请求,你也不用引入gson去转换数据与对象,所有的这一切,Retrofit都已帮你完美实现。

Retrofit之用法
声明:
1.以下例子是基于1.9.0版本的,后面还有
2.0.0版的,下一篇文章我将给大家展示他们之间的区别;
2.列子代码是基于AndroidStudio的。

1.在build.gradle文件里引入Retrofit需要的版本com.squareup.retrofit:retrofit:1.9.0,当然,如果了解RxJava和RxAndroid的童鞋也可以引入io.reactivex:rxjava:1.0.14 、io.reactivex:rxandroid:1.0.1,配合Retrofit使用会更加好用。

2.实现Callback接口
public abstract class RCallback<T> implements retrofit.Callback<T> {
@Override
public abstract void success(T t, Response response);
@Override
public void failure(RetrofitError error) {
Global.ProcException(error);
}
}
看到泛型T,估计大家就知道意味着什么了吧?我们可以直接指定返回的对象了,不用再去解析json数据了!
3.封装请求的载体
public class RestAdapterFactory {
private HashMap<String, SoftReference<RestAdapter>> adapters;
private static volatile RestAdapterFactory maker;
private RestAdapterFactory() {
adapters = new HashMap<>();
}
/**
* 获取创建RestAdapter的工厂
*
* @return
*/
public synchronized static RestAdapterFactory getInstance() {
synchronized (RestAdapterFactory.class) {
if (null == maker) {
synchronized (RestAdapterFactory.class) {
maker = new RestAdapterFactory();
}
}
}
return maker;
}
/**
* 创建RestAdapter
*
* @param url
* @return
*/
public synchronized RestAdapter build(final String url) {
//这就是发起请求的对象
RestAdapter adapter = null==adapters.get(url)?null:adapters.get(url).get();
if (null == adapter) {
final CellInfo cellInfo = Utils.getCellInfo();
//添加一些header
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", String.format("Bearer %s", MainApp.getToken()));
request.addHeader("LoyoPlatform", cellInfo.getLoyoPlatform());
request.addHeader("LoyoAgent", cellInfo.getLoyoAgent());
request.addHeader("LoyoOSVersion", cellInfo.getLoyoOSVersion());
request.addHeader("LoyoVersionName", Global.getV ersionName());
request.addHeader("LoyoVersionCode",
String.valueOf(Global.getV ersion()));
}
};
//指定url,设置log级别,设置拦截器。

adapter = new RestAdapter.Builder().setEndpoint(url).setLogLevel(RestAdapter.LogLevel.FULL).setRequestInte rceptor(requestInterceptor).build();
adapters.put(url, new SoftReference<>(adapter));
}
return adapter;
}
}
对于这个类就不详细解释了,就是封装了RestAdapter,用于发起请求。

4.定义请求接口和发起请求
public interface ITask {
@GET("/task/{Id}")
void getTask(@Path("Id") String Id, retrofit.Callback<Task> cb);
@DELETE("/task/{Id}")
void deleteTask(@Path("Id") String Id, retrofit.Callback<Task> cb);
@GET(FinalVariables.tasks + "template/{id}")
void getTpl(@Path("id") String id, Callback<TaskTpl> cb);
@DELETE(FinalVariables.tasks + "template/{id}")
void deleteTpl(@Path("id") String id, Callback<TaskTpl> cb);
@GET("/task/query")
void getTasks(@QueryMap HashMap<String, Object> body,Callback<PaginationX<Task>> callback);
@POST("/task")
void create(@Body HashMap<String, Object> body, retrofit.Callback<Task> cb);
}
先解释一下一些注解,其实也不用解释,看单词的意思大家也明白啦。

方法上的:@PUT-修改,@DELETE-删除,@POST-创建,@GET-获取。

方法上的注解后面括号里的字符串就是这些请求指向的服务端地址,比如说服务器主地址是http://192.168.3.1:9527,那么getTask指向的地址就是http://192.168.3.1:9527/task,所需参数是
一个String类型的id,查询后返回的对象就是Task。

是不是很简单?
方法变量的:@Path-查询主键,@QueryMap-查询所需的参数集合,可以是任意对象。

比如我们查询Task列表
@Override
public void GetData() {
//这就是@QueryMap需要的参数
HashMap<String, Object> map = new HashMap<>();
map.put("pageIndex", pagination.getPageIndex());//当前页标
map.put("pageSize", isTopAdd ? lstData.size() >= 20 ? lstData.size() : 20 : 20);//需要的数量
map.put("joinType", mJoinType);//任务的类型
map.put("status", mStatus);//任务的状态
map.put("endAt", System.currentTimeMillis() / 1000);//截止时间s
map.put("startAt", DateTool.getDateToTimestamp("2014-01-01", app.df5) / 1000);//起始时间s
//发起请求,这里create方法返回的其实是ITask的代理类RestAdapterFactory.getInstance().build(Config_project.API_URL()).create(ITask.class).getTasks( map, new RCallback<PaginationX<Task>>(){
@overide
public void success(PaginationX<Task> paginationXTasks,Response r){
//获取分页里的数据
ArrayList<Task> tasks=paginationXTasks.getData();
mAdapter.setData(tasks);
mAdapter.notifyDataSetChanged();
}
@Overide
public void failure(RetrofitError e){
}
);
}
这里返回的是我们封装过的PaginationX对象,是一个分页数据封装,里面最终的数据就是Task数组。

简单的几步是不是就完成了一个请求和更新界面?奏是这么任性!
当然,Retrofit的用法肯定不止这些,大家可以去翻墙查询Retrofit官网,上面有很详细的API解释,下一篇我将用RxJava和RxAndroid配合Retrofit使用,进一步简化代码,也让大家进一步了解Retrofit。

现在我们来继续介绍Retrofit配合RxJava、RxAndroid的用法。

配合Rx是为了体现函数式编程的思想,其实用法差别不大,只是在接口里定义方法的返回值有所差别,当然,build.gradle文件里需要导入两个库地址
io.reactivex:rxjava:1.0.14
io.reactivex:rxandroid:1.0.1
下面我们以更新任务为例,观察两种方式的区别
public interface ITask {
//RX模式的方法定义之一
@PUT("/task/{Id}")
Observable<Task> update(@Path("Id") String Id,@Body HashMap<String, Object> body);
//RX模式的方法定义之二
@PUT("/task/{Id}")
Call<Task> _update(@Path("Id") String Id,@Body HashMap<String, Object> body);
//常规异步式的方法定义
@PUT("/task/{Id}")
void update_(@Path("Id") String Id,@Body HashMap<String, Object> body,Callback<Task> callback);
}
方法很容易区别,一个是返回参数在回调接口里,一个是直接返回,但返回的不是最终的对象。

按照常规的调用
HashMap<String, Object> map = new HashMap<>();
map.put("title", title);
map.put("content", content);
map.put("responsiblePersons", Arrays.asList(new Reviewer(mTask.getResponsiblePerson())));
map.put("members", mTask.getMembers());
map.put("actualendAt", mTask.getActualEndAt());
map.put("remindflag", mTask.getRemindTime() > 0);
map.put("remindtime", mTask.getRemindTime());
map.put("reworkflag", switch_approve.isChecked());
if (!TextUtils.isEmpty(mTask.getAttachmentUUId()) && mTask.getAttachments().size() > 0) {
map.put("attachmentUUId", mTask.getAttachmentUUId());
}
if (!TextUtils.isEmpty(mTask.getProjectId())) {
map.put("projectId", mTask.getProjectId());
}
RestAdapterFactory.getInstance().build(Config_project.API_URL()).create(ITask.class).update_( mTask.getId(), map,new RCallback<Task> (){
@overide
public void success(Task task,Response r){
task.setAck(true);
Toast(getString(R.string.app_add) + getString(R.string.app_succeed));
Intent intent = new Intent();
intent.putExtra("data", task);
setResult(Activity.RESULT_OK, intent);
onBackPressed();
}
@Overide
public void failure(RetrofitError e){
}
});
这种调用没什么好说的,和前面一篇文章里的一样,服务器返回的结果会封装在RCallback 里面,并且回调方法在主线程。

下面我们看看按照Rx模式调用之一
HashMap<String, Object> map = new HashMap<>();
map.put("title", title);
map.put("content", content);
map.put("responsiblePersons", Arrays.asList(new Reviewer(mTask.getResponsiblePerson())));
map.put("members", mTask.getMembers());
map.put("actualendAt", mTask.getActualEndAt());
map.put("remindflag", mTask.getRemindTime() > 0);
map.put("remindtime", mTask.getRemindTime());
map.put("reworkflag", switch_approve.isChecked());
if (!TextUtils.isEmpty(mTask.getAttachmentUUId()) && mTask.getAttachments().size() > 0) {
map.put("attachmentUUId", mTask.getAttachmentUUId());
}
if (!TextUtils.isEmpty(mTask.getProjectId())) {
map.put("projectId", mTask.getProjectId());
}
RestAdapterFactory.getInstance().build(Config_project.API_URL()).create(ITask.class).update(m Task.getId(), map)
.observeOn(AndroidSchedulers.mainThread())//指定订阅的线程-主线程
.subscribe(new Subscriber<Task>() {//订阅
//处理完成
@Override
public void onCompleted() {
}
//处理出错
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
//处理过程
@Override
public void onNext(Task task) {
task.setAck(true);
Toast(getString(R.string.app_add)+getString(R.string.app_succeed));
Intent intent = new Intent();
intent.putExtra("data", task);
setResult(Activity.RESULT_OK, intent);
onBackPressed();
}
});
在这里,接口定义的方法直接返回了一个Observable对象,指定这个对象发生订阅的线程,然后订阅事件,最后也是异步回调的,不过在这个过程中,我们可以指定回调方法发生的线程,看起来的话onNext方法和success方法其实没啥区别,都带回了我们需要的数据,但是从代码层次上来看,Rx模式的代码逻辑层次更清楚。

下面我们再看看按照Rx模式调用之二
HashMap<String, Object> map = new HashMap<>();
map.put("title", title);
map.put("content", content);
map.put("responsiblePersons", Arrays.asList(new Reviewer(mTask.getResponsiblePerson())));
map.put("members", mTask.getMembers());
map.put("actualendAt", mTask.getActualEndAt());
map.put("remindflag", mTask.getRemindTime() > 0);
map.put("remindtime", mTask.getRemindTime());
map.put("reworkflag", switch_approve.isChecked());
if (!TextUtils.isEmpty(mTask.getAttachmentUUId()) && mTask.getAttachments().size() > 0) {
map.put("attachmentUUId", mTask.getAttachmentUUId());
}
if (!TextUtils.isEmpty(mTask.getProjectId())) {
map.put("projectId", mTask.getProjectId());
}
RestAdapterFactory.getInstance().build(Config_project.API_URL()).create(ITask.class)._update( mTask.getId(), map).enqueue(new RCallback<Task>() {
@Override
public void success(Task task) {
task.setAck(true);
Toast(getString(R.string.app_add)+getString(R.string.app_succeed));
Intent intent = new Intent();
intent.putExtra("data", task);
setResult(Activity.RESULT_OK, intent);
onBackPressed();
}
@Override
public void onFailure(Throwable error) {
}
});
这里是反回了一个Call对象,调用enqueue的时候就像我们常规模式调用时那样传入callback 接口,从里面获取数据。

当然RX模式的好处远不止这一个,比如说,当我们需要显示等待dialog的时候,常规的异步式调用可能就需要我们维护一个progressdialog,但Rx模式就可以很方便的实现。

Subscriber有一个空实现的方法onStart,是在开始连接服务器之前调用的,我们可以封装一
个commonSubscriber,在onStart方法里去显示一个progressdialog,在onError或onComplete 方法里去自动销毁这个progressdialog。

public abstract class CommonSubscriber extends Subscriber<Serializable> {
private CustomProgressDialog dialog;
public CommonSubscriber(Activity context) {
dialog = new CustomProgressDialog(context);
}
@Override
public void onStart() {
super.onStart();
dialog.show();
}
@Override
public void onNext(Serializable serializable) {
BaseBen baseBen = (BaseBen) serializable;
if(null!=baseBen) {
if
((!TextUtils.isEmpty(baseBen.getErrorCode())&&baseBen.getErrorCode().contains( ErrorUtil.ER ROR_TOKEN_ERROR)) || ((!(baseBen instanceof TokenInfo)) && null == BusApplication.getInstance().getTokenInfo())) {
BusApplication.getInstance().backToLogin();
}
}
}
@Override
public void onError(Throwable e) {
dialog.dismiss();
dialog=null;
}
@Override
public void onCompleted() {
dialog.dismiss();
dialog=null;
}
}
关于Rx相关的知识,我也知道得很有限,有一些比较专业的用法我也不敢信笔涂鸦,有兴
趣的小伙伴可以去查阅一下相关的资料,或许就会有更大的发现。

比如说可以指定多次observeOn,但是subscribeOn只认可第一次;可以用filter方法过滤调一些返回值,可以用flatMap封装下一步需要的参数等等。

最后我贴出一段我用纯Rx模式实现的请求代码供大家参考
//-------------------------------RxJava实现方式开始--------------------------------
Observable.just(schedule)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
//判断行程是否有效,过滤掉无效行程
.filter(new Func1<TourSchedule, Boolean>() {
@Override
public Boolean call(TourSchedule tourSchedule) {
if (null == schedule) {
ptrFrameLayout.refreshComplete();
dialog.dismiss();
ToolToast.showShort("车次信息错误,无法获取订单");
}
return null != schedule;
}
})
//网络请求置于IO线程
.observeOn(Schedulers.io())
//先去请求服务器数据
.flatMap(new Func1<TourSchedule, Observable<CommonListBen<TravelOrderForm>>>() {
@Override
public Observable<CommonListBen<TravelOrderForm>> call(TourSchedule schedule) {
HashMap<String, Object> map = new HashMap<>();
map.put("busNoId", schedule.getBusNoId());
map.put("busId", schedule.getBusId());
map.put("day", schedule.getDay());
map.put("planId", schedule.getPlanId());
return ServiceGenerator.getInstance().create(IOrderForm.class, BusApplication.getInstance().getToken()).getOrderFormList_(map);
}
})
//截获错误,不回调onError方法,使流程继续
.onErrorReturn(new Func1<Throwable, CommonListBen<TravelOrderForm>>() {
@Override
public CommonListBen<TravelOrderForm> call(Throwable throwable) {
return null;
}
})
.observeOn(Schedulers.newThread())
//请求的结果转换为最终需要的数据
.map(new Func1<CommonListBen<TravelOrderForm>, ArrayList<TravelOrderForm>>() {
@Override
public ArrayList<TravelOrderForm> call(CommonListBen<TravelOrderForm> travelOrderFormCommonListBen) {
return null == travelOrderFormCommonListBen ? null : travelOrderFormCommonListBen.getData();
}
})
//处理服务器返回的数据,混合缓存
.flatMap(new Func1<ArrayList<TravelOrderForm>, Observable<ArrayList<TravelOrderForm>>>() {
@Override
public Observable<ArrayList<TravelOrderForm>> call(ArrayList<TravelOrderForm> travelOrderForms) {
if (mDao == null)
mDao = new TravleOrderFormDao(mContext);
//从数据库获取
if (null == travelOrderForms) {
//需要取存数据,以planId为主键
TravelOrderFormData dataDb = mDao.get(schedule.getPlanId());
if (null != dataDb) {
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<TravelOrderForm>>() {
}.getType();
travelOrderForms = gson.fromJson(dataDb.getJson(), type);
}
}
//存数据库
else {
TravelOrderFormData data = new TravelOrderFormData();
data.setPlanId(schedule.getPlanId());
Gson gson = ew Gson();
data.setJson(gson.toJson(travelOrderForms));
mDao.add(data);
}
return Observable.just(travelOrderForms);
}
})
//显示最后获得的结果
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ArrayList<TravelOrderForm>>() {
@Override
public void onCompleted() {
dialog.dismiss();
ptrFrameLayout.refreshComplete();
}
@Override
public void onError(Throwable e) {
dialog.dismiss();
ptrFrameLayout.refreshComplete();
}
@Override
public void onNext(ArrayList<TravelOrderForm> datas) {
travelOrderForms = datas;
handleCustomers();
dataChange();
}
});
//-------------------------------RxJava实现方式结束--------------------------------
同步请求
Retrofit1
public interface TaskService {
@GET("/tasks")
List<Task> getTasks();
}
Retrofit2
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks();
}
可以很清楚的看出他们的区别:1.9.0版本,数据直接通过方法的返回值获取,2.0.0版本则
是把数据包装到Call对象里。

同步方法实在主线程执行的,所以在Android4.0及以上版本会抛出NetworkOnMainThreadException异常,所以在调用的时候要自己放到子线程里去执行,然后通过Handler更新界面。

同步方法里获取数据
Retrofit1
TaskService taskService = ServiceGenerator.createService(TaskService.class);
List<Task> tasks = taskService.getTasks();
Retrofit2
TaskService taskService = ServiceGenerator.createService(TaskService.class);
Call<List<Task>> call = taskService.getTasks();
List<Task>> tasks = call.execute().body();
异步请求
Retrofit1
public interface TaskService {
@GET("/tasks")
void getTasks(Callback<List<Task>> cb);
}
Retrofit2
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks();
}
可以看到,同步和异步请求在2.0.0版本是一样的,1.9.0才有所区别,但是这针对的是请求,数据获取还是有所区别的。

异步方法里获取数据
Retrofit1
TaskService taskService = ServiceGenerator.createService(TaskService.class);
taskService.getTasks(new Callback<List<Task>>() {
@Override
public void success(List<Task> tasks, Response response) {
// here you do stuff with returned tasks
}
@Override
public void failure(RetrofitError error) {
// you should handle errors, too
}
});
Retrofit2
TaskService taskService = ServiceGenerator.createService(TaskService.class);
Call<List<Task>> call = taskService.getTasks();
call.enqueue(new Callback<List<Task>>() {
@Override
public void onResponse(Call<List<Task>> call, Response<List<Task>> response) { if (response.isSuccess()) {
// tasks available
} else {
// error response, no access to resource?
}
}
@Override
public void onFailure(Call<List<Task>> call, Throwable t) {
// something went completely south (like no internet connection)
Log.d("Error", t.getMessage());
}
}
对于异步方法来说,我们最后都必须传入一个Callback接口,实现它的success方法和onFailure方法,不同的是,1.9.0的Callback作为一个参数传递到方法里,2.0.0先返回一个Call对象,再调用Call对象的enqueue方法,传入Callback对象去获取数据。

请求载体的区别
Retrofit1
public synchronized RestAdapter build(final String url) {
//这就是发起请求的对象
RestAdapter adapter = null==adapters.get(url)?null:adapters.get(url).get();
if (null == adapter) {
final CellInfo cellInfo = Utils.getCellInfo();
//添加一些header
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", String.format("Bearer %s", MainApp.getToken()));
request.addHeader("LoyoPlatform", cellInfo.getLoyoPlatform());
request.addHeader("LoyoAgent", cellInfo.getLoyoAgent());
request.addHeader("LoyoOSVersion", cellInfo.getLoyoOSVersion());
request.addHeader("LoyoVersionName", Global.getV ersionName());
request.addHeader("LoyoVersionCode",
String.valueOf(Global.getV ersion()));
}
};
//指定url,设置log级别,设置拦截器。

adapter = new RestAdapter.Builder().setEndpoint(url).setLogLevel(RestAdapter.LogLevel.FULL).setRequestInte rceptor(requestInterceptor).build();
adapters.put(url, new SoftReference<>(adapter));
}
return adapter;
}
Retrofit2
public synchronized <S> S create(Class<S> serviceClass, final String token) {
httpClient.interceptors().clear();
if (token != null) {
httpClient.interceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
// .header("Accept", "applicaton/json")
.header("AccessToken", oken);
// .method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}
//请求发起者
Retrofit retrofit = builder.client(httpClient).build();
return retrofit.create(serviceClass);
}
1.9.0的请求发起者是RestAdapter,
2.0.0的直接就叫Retrofit。

并且在设置header和log级别上也有所差异。

1.9.0可以再builder里直接指定log级别,2.0.0需要引入compile 'com.squareup.okhttp:logging-interceptor:2.6.0' 这个库来设置log级别,当然,也可以自己实现Interceptor来自定义日志输出
class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
String requestLog = String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers());
//YLog.d(String.format("ding request %s on %s%n%s",
// request.url(), chain.connection(), request.headers()));
if (request.method().compareToIgnoreCase("post") == 0) {
requestLog = "\n" + requestLog + "\n" + bodyToString(request);
}
Log.d("TAG", "request" + "\n" + requestLog);
Response response = chain.proceed(request);
long t2 = System.nanoTime();
String responseLog = String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers());
String bodyString = response.body().string();
Log.d("TAG", "response" + "\n" + responseLog + "\n" + bodyString);
return response.newBuilder()
.body(ResponseBody.create(response.body().contentType(), bodyString))
.build();
}
private String bodyToString(final Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
}
当然,Retrofit1.9.0和2.0.0的区别远远不止这些,比如2.0.0版本引入的库比1.9.0要多很多,但是他们最直观的区别应该就是2.0.0完全采用函数式编程思想来实现,即使代码数量过多,也可以很好地控制代码结构和理清代码逻辑,从这点来说,2.0.0还是我比较喜欢的。

期待大家去发掘更多的新奇用法。

相关文档
最新文档