最近研究怎么让自己的程序节省几行代码。仔细想一想,我们在做客户端类的APP时,最基础,大量重复的场景就是:
1.从服务器请求数据2.解析得到的数据并处理加以封装3.将封装好的数据送到UI线程显示到View上。
那么,不可避免的。一次流程我们需要编写:http数据请求,JSON解析处理,异步处理,Handler,业务异常处理,View显示,如果有集合控件,还得写ViewHolder,Adapter。十几个类,N个方法,数百行不止的代码。枯燥无味,费时费力。
那么怎么办,现在流行rxjava,retrofit等框架,自然是非常方便优雅。但是作为程序猿自然得有自己的想法,于是乎有了下面这个即将介绍给大家的“小玩具”。
首先,思考一下上面所说的一套标准流程,真正不可或缺的只有什么?那就是协议,换句话说就是接口,从数据的角度来说:一是从服务器返回的值的协议(json等数据格式到Model模型的映射),二是你请求服务器的参数协议url,参数,头信息等;除此之外,你需要一个数据到视图的映射关系,以及控制层的接口。从java代码的角度来说,你需要1.接口一个,2.带映射关系的(View到值映射,json到对象映射)Model数据模型类,3.没了。
下面就模拟一下下面所说的场景。我将用我写的小玩具完成一次登陆/注册模拟,带参请求服务器登陆,并返回一段JSON数据,包含jsonobject和jsonarray,并显示到textView和listview上。
1.接口,你需要写一个服务接口,里面有你的业务方法,你只需要在接口的抽象方法上的注解上配置一下,接口实现就交给框架动态代理。
public interface ILogin {
@HttpSrcMethod(url = "http://www.baidu.com")
public UserInfoModel login(@Param("name") String name, @Param("pass") String pass, @Header("par3") String par3);
@HttpSrcMethod(url = "http://www.baidu.com",connMode = HttpConnectMode.Post,runMode = HttpRunMode.Async,filters = TestFilter.class)
public UserInfoModel regist(@Param("name") String name, @Param("pass") String pass, @Header("par3") String par3);
}
@HttpSrcMethod表示这个方法将被代理为Http请求业务,注解可以指定url,参数,头的参数映射,可以配置同步还是异步请求,亲求方式Post/Get等,还有过滤器,过滤器将在框架解析返回的string之前对返回的字符串进行一些自定义操作,如解密等。
2.完成带映射的数据模型,注解映射JSON/Xml数据元素到Model的关系,注解映射ViewId到Model的关系
@JsonOrm
public class UserInfoModel implements IHandler{
@BindTextView(R.id.text1)
@JsonString
private String name;
@BindTextView(R.id.text3)
@JsonString
private String pass;
@BindRecycleView(R.id.list)
@BindObj
@JsonSet(clazz = ItemModel.class)
private List<ItemModel> obj;
@Override
public void handler() {
name = name+"handled";
}
}
<pre name="code" class="java">@ListDataSrc(R.layout.item)
public class ItemModel {
@BindText(R.id.item_text)
@JsonString
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
如果Model实现了IHandler接口,那么框架将会在json/xml解析完成之后,调用handle方法,增加对model的控制力,完成一些较为复杂的数据处理逻辑,这也是这一模式的缺点,控制力低,只适合较为简单的流程逻辑。
这样仅仅通过这一个数据模型类,框架就知道了JSON元素和View之间的关系了。
3.控制层的调用。只需要申明一下你的业务接口,让框架生成代理对象,直接调用即可。
public class MainActivity extends AppCompatActivity implements ICallBack{
public final static String json = "{\n" +
" \"name\": \"gy\",\n" +
" \"pass\": \"123\",\n" +
" \"obj\": [\n" +
" {\n" +
" \"name\": \"gy\"\n" +
" },\n" +
" {\n" +
" \"name\": \"gy\"\n" +
" },\n" +
" {\n" +
" \"name\": \"gy\"\n" +
" },\n" +
" {\n" +
" \"name\": \"gy\"\n" +
" }\n" +
" ]\n" +
"}";
private ILogin login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
login = HttpProxyFactory.With(ILogin.class)
.addViewContent("login",this)
.addCallBack("login",this)
.establish();
login.login("gy","123","xxxx");
}
@Override
public void onSuccess(Object o) {
}
@Override
public void onFailed(Object o) {
}
}
同时,你可以设置接口中某个业务的回调,以及视图映射所需要的父容器,一般是Activity或者父视图。
关于方便的保持会话,一般就是请求头内包含一个tokenid,sessionid,这里提供了一个方法设置默认的请求头和参数:
//SessionFactory.Session 只是一个请求头和参数的hashmap
SessionFactory.Session session = new SessionFactory.Session();
SessionFactory.addSession("default",session);
绑定这个Session也很简单,注解上面指定就好了
public interface ILogin {
@HttpSrcMethod(url = "",session = "beforelogin")
public User login(@Param("name")String name,@Param("passwd")String passwd);
}
4.最后,由于框架在解析Model和接口的时候使用的巨量的反射操作,几乎占用了主线程50-100ms的时间。为了优化性能,异步的预加载是必须的,我们在Application中,预先解析好接口和Model的数据结构存在缓存中。大大优化了性能,其实反射的主要耗时在与方法,接口等类型元素的查找获取,而invoke其实对性能的影响可以忽略不计。
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
S.init(this);
MvvmCacheControl.preLoad(new Class[]{ILogin.class});
}
}
附上结果