在做一个互联网应用时, 要考虑技术选型和架构搭建。 先说说技术选型, 以丁丁租房为例在开发时会面对如下问题:
1、图片处理, image loader、picasso、Glide、Fresco, 推荐使用fresco,因为它使用三级缓存、占内存更小;
2、http通信, 开源框架有很多例如volley,retrofit,okhttp等等, 用法都很简单也类似, 推荐使用OkHttp,它支持SPDY;
3、崩溃日志采集, 免费库也有很多, 腾讯bugly、友盟、Fabric。我们使用的是Fabric, 挺好用的。
4、即时聊天, 三方库也有很多, 就不多说了。 我们用的是leancloud, 因为它免费:), 而且效果还可以。
5、控件, 根据UI需要可以自己写或用别人写好的三方库, 例如Materialdialog,MPAndroidChart, WheelView等等。
6、进程内部消息传递, 例如跟Service交互的binder、或者LocalbroadcastReceiver(基于主线程handler实现的,所以不会被其他进程收到,比较安全!)、三方库EventBus和观察者模式等等。
7、支付, 可以用支付宝、微信的接口。
8、埋点, 这个就很重要了, 产品经理每天都在盯着这个统计数据, 用他们的话叫做数据引导决策, 有时前端也会弄ABTest, 目的是做出让用户更喜欢的东西。 我们用了Countly, 因为它开源, 后台可以自己做, 埋点数据可以传到自己的服务器。 毕竟一些敏感的数据是不想让三方如友盟知道的。
9、bug热修复, 目前有很多技术方案, 我们参考了HotFix的方式在项目中落地, 网上有人说适配有问题, 但我们还没碰到。。。
10、插件化, 应用典型案例就是支付宝、微信。当app功能非常多、代码量很大(超过65535个方法)时要考虑, 我们是计划实践一下, 但因为公司突然倒闭, 没来得及做。
11、程序框架, 对比mvc、mvp和mvvm模式, 我觉得mvp模式比较合适并落地到丁丁租房app里, 因为mvp很好的实现了代码解耦、逻辑分层, 下层对上层透明, 每层只关心自己那点事情就够了。
12、UI标注,推荐使用标你妹啊网站、app.zeplin.io网站。
下面丁丁租房架构图, 最上面一层是各个功能。
代码结构如下, 按照业务划分一级目录, 二级目录是按照android各组件区分。
从二级目录看到activity实例化presenter并保存该引用, presenter通过Activity传进来的interface回调到Activity, presenter实现网络接口。
下面附干货源码, 供参考:
Model代码:
/** * http网络相关工具类, 目前使用三方库OkHttp库 */ public class NetworkUtils { //网络接口执行周期打点 private static class DotNet { public String tag; //网络接口的标签 public long timestamp; //时间戳 public String uuid; } private static OkHttpClient sInstance; //初始化OkHttp实例 static { sInstance = new OkHttpClient(); sInstance.setConnectTimeout(10, TimeUnit.SECONDS); //连接超时时间 sInstance.setWriteTimeout(10, TimeUnit.SECONDS); //上传文件超时时间 sInstance.setReadTimeout(20, TimeUnit.SECONDS); //下载文件超时时间 } /** * 格式化请求body * * @param object, 查询用的参数,Map or entity */ private static RequestBody formatRequestBody( Context context, Object object) { //JSON.toJSONString(object)方法默认执行去除value为null的字段 /*Iterator iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); //编辑房源时可以传空字符串 if (entry.getValue() == null) { iterator.remove(); } }*/ LinkedHashMap baseMap = GatewayUtils.getInstance().getBase(context); //获取base参数 Iterator it = baseMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (entry.getValue() == null || StringUtils.isNull(entry.getValue().toString())) { it.remove(); } } String param = JSON.toJSONString(object); //生成param参数 String sign = ParamBuild.getSign(object, baseMap); //计算sign参数 baseMap.put("sign", sign); String base = JSON.toJSONString(baseMap); RequestBody formBody = new FormEncodingBuilder() .add("base", base) .add("param", param) .build(); return formBody; } /** * 格式化请求body * * @param object, 查询用的参数,Map or entity * @param dotNet, 打点要用的参数 */ private static RequestBody formatRequestBodyExt( Context context, Object object, DotNet dotNet) { //JSON.toJSONString(object)方法默认执行去除value为null的字段 /*Iterator iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); //编辑房源时可以传空字符串 if (entry.getValue() == null) { iterator.remove(); } }*/ LinkedHashMap baseMap = GatewayUtils.getInstance().getBase(context); //获取base参数 Iterator it = baseMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (entry.getValue() == null || StringUtils.isNull(entry.getValue().toString())) { it.remove(); } } try { //网络接口执行周期打点时的参数 String uuid = (String) baseMap.get("uuid"); dotNet.timestamp = System.currentTimeMillis(); dotNet.uuid = uuid; } catch (Exception ex) { ex.printStackTrace(); } String param = JSON.toJSONString(object); //生成param参数 String sign = ParamBuild.getSign(object, baseMap); //计算sign参数