1. 业务请求框架概述
架构的设计总是和业务的发展相结合和适应的。由于不同业务的差异性导致接口、协议等都有不同的需求,给多款App设计出一个拥有干净API和高度内耦合的网络层成为了一项挑战,而这个设计也将直接影响我们App业务工程师们的开发效率。
先来看与业务相结合的网络层设计
与业务相连接最紧密的地方必然是输入与输出,而网络层的功能无疑是接受输入的数据,挑选一个相应的通道组装数据发送给服务器,然后将服务器返回的数据返回给上一层,即输出数据。接下来我们从数据的角度来看看在网络层设计中需要考虑些什么样的问题。这里我们会从以下三个方面来进行阐述:
- 数据输入
- 数据回调
- 数据转换
数据输入
首先是输入过程。业务数据调用网络层接口时可以称之为输入,这里一般会有两种形式的设计:
第一种,很多时候会被称为集中式的API处理,即将一些经常使用的网络层调用的代码封装成一到两个函数供上层调用。上层输入相关的参数便能取得相关的回调。如以下函数:
void generalRequest(String url, Map<String, String> params, String requestType, Callback callback);
另一种形式的设计,则采用一种继承形式设计每一个API,而每一个API都对应一个类,这个类中将该API的所有参数都设定好,很多时候我们称这种为分布式的API处理。一个比较通用的BaseAPI如下:
public abstract class BaseApi<T> {
public abstract String getApiUrl();
public abstract Map<String, String> getParams();
public abstract String getRequestType()
public abstract T execute();
}
每一个具体的API都可以继承自这个BaseAPI,当上层业务需要进行网络调用时,实例化一个需要调用的API接口,然后调用执行函数,对返回的数据进行编码,最后通知业务回调
这两种网络层接口的设计其实都是对应不同的业务所产生出来的思维,因此必然都有其优缺点。
例如,第一种形式的接口其优点在于简单粗暴,适用于业务逻辑相对简单并且统一的 API网络接口。但是缺点也非常明显,一旦遇上稍微复杂一些的网络接口情况,便需要在里写入大量的逻辑来达到目的。
第二种的设计则优雅得多。将大量的配置逻辑都放在了另一个类文件中进行设计,而将配置放在类中还有另一个好处,那便是可以增加许多平时不使用的可配置项,来增加整个网络层的可扩展性;与此同时,每一个API对应了一个不同的类的设计,又可以让不同的API可以有不同的表象,在集中式的API设计时,更多的时候是传参,那么现在采取分散式API设计时,由于每个API先继承BaseAPI,然后再在子类中去覆盖每个需要配置的函数,因此看起来每个API都更像是一个配置的过程。配置完成后的每个API,过去的方式可能是每个API都对应一个APIManager,反馈到NetworkEngine上。
不过这种形式的设计也存在触目惊心的问题,那便是类爆炸。如果是小型的APP,则问题并不是那么明显;而如果是中大型APP的话,动则上百个API,会使得后期的维护变得有些吃力。
数据回调
说完了输入问题,接下来输出的设计。在输出部分的设计中,可以说是八仙过海各显神通。网络层的传输大多以异步加载为主,即服务器响应后由网络层来负责将数据推给上层业务线程,上层业务线程经过数据处理和转换后,回调UI线程
数据转换
数据回调的问题已经基本解决,但是新的问题也摆在了我们的面前:上层该看到怎样的数据?
在这里我们会发现非常多的应用场景,比如大多数情况下,业务层都希望返回的是与其自身相关的数据结构(Model实例),在这样的前提下能够非常地方便地对本地的数据进行相关的操作.从以上各种场景中我们可以看到,业务所需的数据形式非常多变,因此最好的方式还是交给上层自己去处理。一种常见的方法就是进行返回数据的转换,将JSON或者XML等格式转成所需要的数据格式以方便上层业务继续处理。还有一种是在API本身就实现好这个所描述的转换函数,这样会让API的层次更加清晰,一般通过Gson,Jackson等库进行转换实现。
业内其他设计可以参考
Retrofit API
2. Business Task 业务请求框架
业内常用的业务请求框架:
- AsyncTask
还有一种常见的情况就是,在Activity和Fragment中使用AsyncTask类:
class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
// do something time-consuming
}
protected void onPostExecute (Void result) {
// do something in ui
}
}
AsyncTask缺点:
1)AsyncTask内部类会持有外部类的隐式引用。由于AsyncTask的生命周期可能比Activity的长,当Activity进行销毁AsyncTask还在执行时,由于AsyncTask持有Activity的引用,导致Activity对象无法回收,进而产生内存泄露。
2)AsyncTask在fragment使用时,onPostExcute执行回来后,需要判断frament的生命周期,经常会碰到NoActivity的异常。在Acivity中使用时,如果activity已销毁,task的回调也会调动,这样会导致不必要的回调等
3) 代码结构容易与UI耦合较紧,代码会显得层次不清晰
- Volley
如Volley封装了很多网络的请求,如下面的代码:
StringRequest request = new StringRequest(
Request.Method.POST,
"http://***",
new Response.Listener<String>() {
@Override
public void onResponse(String s) {
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
}
}
);
Volley的缺点:
1) 由于Volley默认封装了网络的请求,因此对于一些定制化的网络请求,我们需要继承HurlStack
2) Volley不恰当回调使用会造成与Asynctask一样的内存泄露
3) Volley一般都需要我们在Activity和Fragment生命周期中主动调用Cancel, 但是其Cancel机制只是在网络请求触发前,如果该请求已经开始则只是不触发回调函数的调用,其回调引用会一直持有至网络请求完成
为了很好的解决上述一些问题,我们实行了一套自己的业务请求框架,咱称之为 BT
- BusinessTask
1) 底层实现优先级线程池,可以根据业务自定义线程池个数,每个任务的优先级,运行时取消任务等功能
2) 上层实现了Task, Task是线程池中执行的执行单元
3) 网络相关请求我们封装了BaseAPI相关类,底层调用NetworkEngine,该NetworkEngine我们可以灵活定制。 APITask包装了具体API请求,在线程池中进行执行,执行完成返回的数据我们直接进行Gson相关的转换,转换成业务相关的实体类,完成业务的回调,最后通知 UI层的回调
4) UI相关回调
默认回调函数都是弱引用,在框架层里杜绝了内存泄露,而且我们在UI基类层中判断了Fragment和Activity的生命周期,将业务数据回调给具体的UI组件时提前进行了判断,防止了不必要的回调。
下图是Task 架构图
BT 框架不仅可以用于网络请求,还可以用于其他耗时业务。
基于BT框架的API请求框架
BaseAPI中调用网络引擎接口
JsonApi 为模板类,里面将服务器返回的json串直接转换成业务需要的实体类
每个具体Api直接继承JsonApi, 添加相应的请求参数和Url
ApiTask继承与BusinessTask, 里面包含具体的Api, Task执行完成后,通知UI层进行相应的业务处理,UI回调接口使用统一的一个接口,各种 Task使用ID进行区分。
具体API继承与模板 JsonApi类,返回的实体类作为模板参数:
public class PicClassTagsListApi extends JsonApi<PicClassTags> {
public PicClassTagsListApi() {
super(new CommonUrl("http://***", RequestType.GET));
}
}
API请求执行函数
public void getPicClass(BusinessCallback callback){
PicClassTagsListApi picListApi = new PicClassTagsListApi();
new ApiTask(BusinessId.Pic.ID_PIC_CLASSES, picListApi, callback).execute(this);
}
3. LePhoto App
该 App使用了 Task Api Framework ,该框架和app全部开源:GitHub地址
App 截图如下:
使用Task Api Framework让我们开发一个稳定强大的App变得异常容易且结构清晰,Enjoy It 吧!!!