使用MVP+RxAndroid+DroiBaaS打造云后台App—校园日记
为什么想做校园日记?
前段时间支付宝的校园日记功能火爆异常,但是却昙花一现,可是在社会上还是引起了一阵自媒体浪潮,其实这就是人的本性的释放,人的本性就有喜欢嘚瑟,爱表现自己的成分。在我理解中大部分能火起来的App都有能抓住人的一部分本性需求,所以我就想开发一个校园日记的App,让它成为最时尚的大学生社交活动App,专为广大在校童鞋们打造的校园日记分享软件。可以上传自己的自拍照片、美食图片、心情感想等日记,实现随时随地分享自己,展现自己的需求。
APP功能分解:
核心功能就是个人日记的展示,其实这个最终的样子做出来应该和朋友圈非常类似
为什么选用MVP+RxAndroid+DroiBaaS
技术架构选型:
对程序进行架构设计的原因,归根到底是为了提高生产力。通过设计使程序模块化,能够更简单的读懂code以及方便维护和测试。整体的App架构选用MVP来搭建,结合最近比较火热的RxAndroid实现观察者事务模式就能够做到模块内部的高聚合和模块之间的低耦合,模块内被的高聚合。
由于开发的应用需要搭建云服务器和数据库,所以也选用了最近比较流行的一站式后端云服务DroiBaaS来实现所有云后台功能。后面会讲到具体用到哪些功能和怎么来使用这些功能。
为什么选用MVP模式?
以上是MVP的工作原理图。其中Presenter操作View和Mode都是通过接口来实现直接的调用。
传统的MVC模式很难把View和Controller分开,总是直接在View的事件响应函数里完成了Controller的代码,而MVP就完全解决了这个问题。MVP的工作流程如下:Presenter负责逻辑的处理,Model提供数据,View负责显示。
作为一种新的模式,在MVP中View并不直接使用Model,它们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部。这样的话最大可能降低了View和Model之间的耦合性,维护和测试起来都是异常的简单方便。
为什么选用RxAndroid?
最主要是两个字简洁,RxAndroid是RxJava的扩展,它的异步调用随着程序逻辑变得越来越复杂,它的链式调用依然能够保持简洁。
RxAndroid的回调方法主要有三个,onNext(),onError(),onCompleted()。
• onNext() 对于Subscribler我们可以理解为接收数据。
• onCompleted() 观测的事件的队列任务都完成了,当不再有onNext()发送数据时,onCompleted事件被触发。
• onError() 当事件异常时响应此方法,一旦此方法被触发,队列自动终止,不再发送任何数据。
为什么选用DroiBaaS?
在这之前我的云后台都来自于阿里云+后端工程师,但是我只是个Android工程师,所以我需要一个更加简单方便的云后台生产工具。我选择云后台,希望能满足我以下几个要求:
- 服务器环境我不会搭建,所以更别提维护了,比如CentOS+Nginx+PHP+MySQL,我也只是听说而已
- 我更不会写Server端的Code,因为我只会安卓App开发,而且这应用只是我个人开发,也找不到其他人来帮我写server code
- 最好能有现成的可视化管理后台,这样以后管理和运营起来也方便
- 花钱尽量少,最好免费,毕竟是个人兴趣和尝试,不希望试错成本太高
- 能一站式尽量一站式,虽然我也可以用友盟的统计+极光的推送+酷传的代发布+百度的广告+啥啥啥,不过毕竟麻烦么不是。。
综合这些需求,我发现最近新鲜出炉的专为APP开发者提供一站式整合云后端的服务——BaaS比较适合我来使用。无需租用服务器和开发服务器端程序,只需集成BaaS平台提供相应SDK就能够实现各种云后台的功能,比如云数据库,用户系统搭建,推送通道,用户反馈收集,版本管理和数据统计的功能,这些功能对于App的开发以及之后的运营都是必须的。目前国内的几家BaaS云服务提供商,比如leanCloud、DroiBaaS、Bmob、Maxleap,目前都处于创业阶段,因为本身BaaS还处于一个概念期,到普及还需要一段时间,但是用过之后真心觉得相当好用。我相信选用BaaS来搭建App的云后台这将是之后个人开发者以及中小企业开发者的趋势。
DroiBaaS相比其他几家的优势在于:
- 提供沙箱和生产两种模式,沙箱完成调试再发布生产环境上线,避免了调试和测试对正式版本的数据污染
- DroiObject使用相当的方便,注解的编程方式相比其他几家还是有不错的便利性
- 有渠道和广告背景,App开发出来之后能够提供一定的推广和变现的帮助
- 文档比较全面而且详细,SDK集成方式简单,打电话咨询过,客服态度不错,很耐心也很专业
- 免费额度相比较其他几家还是比较有优势的,虽然我也不知道会用到多少,但是多一点总归是好的
系统框架设计:
代码架构如下
使用MVP模式来开发的好处就是代码架构非常的清晰明了,个人还是比较注重代码的逻辑性以及可读性
用到的框架及生产工具
日记的展示界面用了SuperRecyclerView——使RecyclerView更加容易使用的Android类库
图片加载与缓存用了Glide
DroiBaaS的网络请求都是基于OKHttp的,所以OKHttp和OKio是必须用到的网络框架
Json的生成和解析用的FastJson
响应式编程用的是RxAndroid
高度整合封装的云服务BaaS作为第二代云计算的产物,为App的云后台开发提供了非常便利的生产工具,提高了开发效率、缩短了上线时间、降低了开发成本,这必将是一个潮流和趋势,我还是比较看好的。
所有的云端功能,如推送、自更新、用户反馈、统计、云数据、用户管理功能全部是用DroiBaaS的SDK来实现
日记展示UML架构设计:
MainActvity继承CircleContract.View接口,
CirclePresenter继承CircleContract.Presenter接口
MainActvity 生成一个CirclePresenter对象同时把自身传入CirclePresenter
MainActvity调用CircleContract.Presenter的各种数据获取接口,CirclePresenter从云端获取到数据后调用CircleContract.View的界面更新接口通知MainActivity来刷新View
整个MVP架构相当的清晰明了,使用MVP最大的好处就在此处,代码简洁,同时简化了Activity的逻辑,利于以后的调试和单元测试,新功能加起来也非常的方便
数据库设计如UML图所示
主要是四个主要交互数据类,这个四个类同时也是后DroiBaaS云后台数据库保存的数据类
CircleItem:日记内容Data
FavortItem:用户点赞Data
CommetItem:用户评论Data
User:用户Data
详细代码设计
日记MVP+RxAndroid代码如下:
整个工程代码比较多,我在这里只贴了日记展示关键逻辑的代码,整个工程的源码请参考文档最后的GitHub的链接
CircleContract.java
Model和View的中间接口类
public interface CircleContract {
interface View extends BaseView{
void update2DeleteCircle(String circleId);
void update2AddFavorite(int circlePosition, FavortItem addItem);
void update2DeleteFavort(int circlePosition, String favortId);
void update2AddComment(int circlePosition, CommentItem addItem);
void update2DeleteComment(int circlePosition, String commentId);
void updateEditTextBodyVisible(int visibility, CommentConfig commentConfig);
void update2loadData(int loadType, List<CircleItem> datas);
}
interface Presenter {
void loadData(int loadType);
void deleteCircle(final String circleId);
void addFavort(final int circlePosition,final String circleId);
void deleteFavort(final int circlePosition, final String favortId);
void addComment(String content,final CommentConfig config);
void deleteComment(final int circlePosition, final String commentId);
void showEditTextBody(CommentConfig commentConfig);
}
}
CirclePresenter.java
此类使用RxAndroid从云端获取数据再发回给View进行异步展示,在这个类中可以看出使用RxAndroid处理异步逻辑非常得心用手,推荐大家使用。
public class CirclePresenter implements CircleContract.Presenter{
private CircleContract.View view;
private static int index = 0;
public CirclePresenter(CircleContract.View view){
this.view = view;
}
@Override
public void loadData(final int loadType){
if(loadType == 1){
index = 0;
}
getCircleData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<CircleItem>>() {
@Override
public void onCompleted() {
view.hideLoading();
}
@Override
public void onError(Throwable e) {
view.showToast("网络错误!");
}
@Override
public void onNext(List<CircleItem> data) {
view.update2loadData(loadType, data);
}
});
}
/**
*
* @Title: deleteCircle
* @Description: 删除动态
* @param circleId
* @return void 返回类型
* @throws
*/
@Override
public void deleteCircle(final String circleId){
deleteCircleData(circleId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Boolean>() {
@Override
public void onCompleted() {
view.hideLoading();
}
@Override
public void onError(Throwable e) {
view.showToast("网络错误!");
}
@Override
public void onNext(Boolean result) {
if (result) {
view.update2DeleteCircle(circleId);
}else{
view.showToast("删除数据失败,请重试!");
}
}
});
}
/**
*
* @Title: addFavort
* @Description: 点赞
* @param circlePosition
* @return void 返回类型
* @throws
*/