谈起网络框架,大家基本上都很熟悉。从Google推出的Volley,到大名鼎鼎的OkHttp、Retrofit,还有比较基础的HttpUrlConnection、android-async-http,以及已经被废弃掉的HttpClient。应该也都经历过每次更换框架时,牵一发而动全身的那种痛苦,明明只是想换个网络框架而已,结果大部分文件都被改动了。这就是本文想解决的一个问题:搭建一个网络架构,通过修改有限的几个文件,达成更换网络框架的目的。
我们从最基础的网络请求开始思考,其实网络请求的过程和去酒吧喝酒的过程差不多。你向服务员点一杯酒,服务员告诉调酒师,而你要耐心等待到拿到酒之后再去做你想做的事情。
这其中包含三个主要元素:请求参数(params)酒的类型
、结果回调(callback)拿到酒之后你要做的事情
和请求任务(task)调酒师调酒的过程
。不管用什么框架,请求参数基本上不用改动,就像去不同的酒吧你只要说来杯玛格丽特
服务员都会知道。但结果回调会有变化,你可以独自品味,也可以趁机把妹
。请求任务也会大不一样,有的多个调酒师、有的一个调酒师、有的调的快、有的调的慢
。综上我们考虑对结果回调和请求任务做下封装,使不同的框架最终都要调用这两个封装对象,这样更换框架时只要修改这两个对象和接口实现的类,具体调用的地方不用做任何修改。
基础版本
首先我们定义一个结果回调抽象类NetCallback,这样根据不同的框架可以继承不同的实现,调用的地方就不用变动了。
public abstract class NetCallback {
/**
* 请求成功时回调接口
* @param t 返回结果
*/
public abstract void onSuccess(T t);
/**
* 请求失败时回调接口
* @param e 错误信息
*/
public abstract void onError(Throwable e);
}
再定义一个请求任务类NetTask,所有任务都要能够取消,这样界面退出时能够避免一些网络流量的浪费。
public class NetTask {
/**
* 取消网络请求
*/
public void cancel();
}
这样基本架构就算完成,可以进行接口调用了。不过调用接口之前,要先添加一个接口接口类,这样可以在不同的地方调用同一个接口,减少复制粘贴的操作。接口实现类Api:
public class Api {
/**
* 豆瓣电影Top250
*/
public static NetTask getTop250(int start, int count, NetCallback callback{
return new NetTask();
}
}
OK,现在可以进行调用了。为了避免重复添加代码,我们先定义一个基类TaskActivity,进行请求任务的管理:
public class TaskActivity extends BaseActivity {
private ArrayList tasks;
/**
* 添加请求任务
* @param task 请求任务
*/
public void addTask(NetTask task) {
if (tasks == null) {
tasks = new ArrayList<>();
}
tasks.add(task);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (tasks != null) {
for (NetTask task : tasks) {
if (task != null) task.cancel();
}
}
}
}
接口调用类,TaskActivity的子类MainActivity:
public class MainActivity extends TaskActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
getTop250();
}
public void getTop250() {
NetCallback callback = new NetCallback() {
@Override
public void onSuccess(Top250Responseresponse) {
DebugLog.e("onSuccess");
}
@Override
public void onError(Throwable e) {
DebugLog.e("onError : " + e.getMessage());
}
};
addTask(Api.getTop250(0, 20, callback));
}
}
一个登录接口就这样调用完成了。请求参数(params)、结果回调(callback)和请求任务(task),网络请求是不是既清晰又简单方便?!
Retrofit实现
我们用Retrofit实验一下,看看该架构的实用性(Retrofit的相关依赖这里不做介绍,直接使用)。首先添加Retrofit相关代码,Retrofit接口类AppSever:
public interface AppServer {
@GET("top250")
Call getTop250(@Query("start") int start, @Query("count") int count);
}
Retrofit对象单例封装RetrofitSender:
public class RetrofitSender {
private AppServer server;
//构造方法私有
private RetrofitSender() {
//手动创建一个OkHttpClient并设置超时时间
OkHttpClient.Builder okBuilder = new OkHttpClient.Builder();
okBuilder.connectTimeout(30, TimeUnit.SECONDS);
okBuilder.readTimeout(31, TimeUnit.SECONDS);
Retrofit retrofit = new Retrofit.Builder().client(okBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.douban.com/v2/movie/")
.build();
server = retrofit.create(AppServer.class);
}
//获取单例
public static AppServer getInstance() {
return SingletonHolder.INSTANCE.server;
}
private static class SingletonHolder {
private static final RetrofitSender INSTANCE = new RetrofitSender();
}
}
Retrofit接口回调类AppCallback:
public abstract class AppCallback implements Callback {
@Override
public void onResponse(Call call, Response response) {
onSuccess(response.body());
}
@Override
public void onFailure(Call call, Throwable t) {
onError(t);
}
//请求成功回调
public abstract void onSuccess(T response);
//请求失败回调
public abstract void onError(Throwable e);
}
添加完毕,调用Retrofit需要做哪些修改呢?首先NetCallback继承AppCallback,仅仅一行改动:
public abstract class NetCallback extends AppCallback {
//其他不用改动
}
然后包装Retrofit的Call,修改NetTask:
public class NetTask {
private Call call;
public NetTask(Call call) {
this.call = call;
}
@Override
public void cancel() {
if (call != null && !call.isCanceled()) {
call.cancel();
}
}
}
最后修改接口实验类Api:
public static NetTask getTop250(int start, int count, NetCallback callback) {
Call call = RetrofitSender.getInstance()
.getTop250(start, count);
call.enqueue(callback);
return new NetTask(call);
}
OK,具体调用的地方根本没有做任何改动,网络框架已经切换到Retrofit。以后是不是再也不担心更换网络框架了?!
Retrofit & RxJava
我们再来实验一下Retrofit结合RxJava的网络框架,这次需要改动多少呢?首先修改Retrofit接口类AppSever,函数返回类型修改为Observable:
public interface AppServer {
@GET("top250")
Observable getTop250(@Query("start") int start, @Query("count") int count);
}
Retrofit对象单例封装RetrofitSender增加一行:
Retrofit retrofit = new Retrofit.Builder().client(okBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
//下面是新增加的一行
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl("https://api.douban.com/v2/movie/")
.build();
修改Retrofit接口回调类AppCallback:
public abstract class AppCallback extends Subscriber {
public abstract void onSuccess(T response);
@Override
public abstract void onError(Throwable e);
@Override
public void onNext(T t) {
onSuccess(t);
}
@Override
public void onCompleted() { }
}
然后包装RxJava的Subscription,修改NetTask:
public class NetTask {
private Subscription task;
public NetTask(Subscription task) {
this.task = task;
}
/**
* 取消网络请求
*/
public void cancel() {
if (task != null && !task.isUnsubscribed()) {
task.unsubscribe();
}
}
}
最后修改接口实现类Api:
public class Api {
public static NetTask getTop250(int start, int count, NetCallback callback) {
Subscription task = RetrofitSender.getInstance()
.getTop250(start, count)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(callback);
return new NetTask(task);
}
}
OK,运行,一切正常!完全没有动业务逻辑相关代码,网络框架的更换轻松顺畅。再也不用担心更换网络框架时伤筋动骨肉相连绵不断的改动了!!!