Android网络框架封装的认知
2015年10月27日 15:04在开发应用中网络模块是项目中很重要和复杂的一部分. 从零开始写一个网络请求框架是一件吃力不讨好的事情, 也有重复造轮子的嫌疑. github上有很多优秀的开源网络请求框架例如:
我们需要在这些第三方框架的基础上再封装, 目的是为了实现: 统一处理, 简化调度. 下面就是年青人对网络框架封装一些粗鄙的认知.
1.对外调用接口统一
项目快速的开发过程中没有认真做框架选型调研, 在后期开发中随着框架不足的暴露, 迫使开发者去使用新的框架. 但替换旧框架的过程肯定是异常艰辛的, 所以有必要在前期就封装一套统一稳健的对外调用接口.
这有点类似于BaseActivity的作用, 其实可以理解这层封装是一个代理模式 . 实现过程可以更粗暴, 直接将逻辑代码写在调用类中, 如果要替换新的新的网络框架也是粗暴重写.
对外调用方式封装可以按开发者喜好来, 比如我比较喜欢链式编程, 那我可以构建一个Fluent interface .
Query.api(api)
.put(key, value)
.put(key, file)
.post(new Query.OnQuery() {
@Override
public void onFailed(Error e) {
//...
}
@Override
public void onSuccess(Model data) {
//...
}
});
2.数据解析和业务实现抽离
老是听到程序员抱怨产品改需求(说的就是我自己), 产品改需求就会在一定程度上实地程序员的辛苦变成徒劳. 也不尽然, 需求的改变更多的是发生在业务逻辑和页面实现上, 但服务器接口是相对稳定的, 所以可以将数据解析单独抽离出来. 在一个单独的类中实现数据解析封装, 这样即符合单一职责原则(SRP )也可以实现代码重用(DRY ).
还有在面临的解析的数据是多层级多广度的, 而在网络请求做解析抽离后只能返回单个对象时, 可以在解析时组装业务层需要的Bean(业务)实例并返回. 当解析数据容量很大或者需要做数据缓存等耗时操作需要将整个过程异步处理, 然后再返回给UI线程. 永远不要相信服务器不会挂, 就算是服务器挂了我们客户端也要接住掉下来的锅. 不要服务器数据返回数据一有问题客户端就挂了. 所以要在解析过程中做异常处理或者添加数据校验.
关于数据实体类, 我见到的大部分的项目中实体类中有很多get/set的方法,很没有必要. 一是浪费人力(虽然Android Studio可以自动生成代码), 二是新增性能消耗. 在开发中大部分的实体类只是为了读取一下数值, 完全没有必要添加访问控制(真的需要时可以酌情使用), 推荐直接使用public暴露出来(具体可以查看google给出的建议 ).
这里给出一种外部调用的范例:
解析类
/**
* 登录信息解析类, 包括绑定接口(api)和数据解析(parse)
**/
public class LoginQuery extends Query<LoginModel> {
@Override
public String api() {
return "this.is.request.api";
}
@Override
protected LoginModel parse(String rawJsonData, boolean isFailure) {
return parseJson(rawJsonData, LoginModel.class);//这是解析过程
}
}
数据模型
/**
* 登录接口返回数据模型
**/
public class LoginModel {
/**
* 如果服务端字段定义不符合java规范
* 或者工程要代码混淆时可以使用Gson注解控制
**/
@SerializedName("user_name")
public String userName;
@SerializedName("user_id")
public String userId;
@SerializedName("access_token")
public String accessToken;
}
调用
/**
* 返回的数据类型有泛型约束
**/
LoginQuery.with(context)
.put(key,value)
.post(new Query.Callback<LoginModel>(){
@Override
public void onFailed(Error e) {
//...
}
@Override
public void onSuccess(LoginModel data) {
//...
}
});
3.规范
规范是个好事, 最好还是在做事情之前就定好了(就开发而言).
对于http接口的一些规范, 就是数据包规范. 比如: 接口API要拼接在url里; request parameter body里面就好就只存在和业务相关的请求参数, 其他基础数据可以放在header中.
还有就是千万不要在Fragment的onCreate()
中发起网络请求, 一般认为网络请求消耗的时间是会远远大于界面绘制的时间. 然后在网络请求返回时以为视图已经初始化完成, 但真的能保证吗?如果我在封装架构里添加一层网络状态检查, 没有网络就直接回调. 接下来会发生什么, 就看个人修养了. 另外提醒一下, 如果是在Fragment里发起的网络请求在回调回来之后最好要做生命周期判断, 否则有可能报IllegalStateException
异常.
new Query.Callback<LoginModel>(){
@Override
public void onFailed(Error e) {
//...
}
@Override
public void onSuccess(LoginModel data) {
final Activity activity = getActivity();
if (activity != null && !activity.isFinishing()) {
//页面没有被回收
//...
}
}
}
ps: 外部调用我都想好了, 谁来写内部实现呢(算了近期我打算自己去实现一个) …
pps: 毕竟是年青人, 说得不好的要给他指出来.
20160415更新:
网络库封装地址: http-query