本文基于MVP整理出了一套适合自己的架构
0 Android架构系列文章
该系列文章会不断更新Android项目开发中一些好的架构和小技巧
系列一 Android架构系列-基于MVP创建适合自己的架构
系列二 Android架构系列-如何优美的写Intent
系列三 Android架构系列-开发规范
系列四 Android架构系列-封装自己的okhttp
系列五 Android架构系列-MVP架构的实际应用
1 为什么选择MVP
MVP架构是当前比较成熟的Android架构,还有其他架构比如最初始的MVC和MVVM。MVC相对于较为落后,MVVM使用DataBind,普及性不如MVP。所以最终决定自己设计的框架是基于MVP思想进行总结的框架。
选择MVP框架的原因之一也是google官方的示例中MVP sample已经是完成,证明google官方对于MVP的承认度。
官方项目地址:
https://github.com/googlesamples/android-architecture
一个较为详细的官方项目源码解析的文章:
http://www.infoq.com/cn/articles/android-official-mvp-architecture-sample-project-analysis
2 MVP简介
具体的MVP架构相关文章网上已经非常多了,具体的可以自行查找。MVP的存在主要是由于普通MVC架构会导致项目中activity过于臃肿,当项目越来越大后,代码可读性大大降低。
MVP的思想是将activity作为view层,只负责与xml的渲染和监听事件,具体处理数据逻辑放到一个新定义的Present层。减少了activity负责的事情。并且可以强迫开发者养成分模块功能开发的思想。开发前设计好功能模块,而不是像以前一样写流水账一样写代码。从头写到尾。
3 我的总结
自己根据MVP的思想和一些好的源码总结了一套适合字的框架。真正的架构是依赖义务存在的,所以建议大家能总结出适合自己项目的代码。
3.1 目录分配
在目录分配上决定采用根据功能模块进行划分,而不是所有activty在一个目录的方法。类似google的例子:
原因几点:
- 功能模块划分更为清晰,对于以后代码阅读和新人接手更好
- 适用于模块化开发,比如以后又是一个新项目,老项目的登录模块、用户模块、论坛模块等待可以整个复制出来重用
- 虽然网上提过按照多个模块划分可能会有公用的页面。我认为复制一份也没什么,不会造成很大的冗余代码,并且对于页面来说万一以后某个模块页面有自定义修改不会对其他影响。(毕竟页面的灵活性要求很高,不适合架构抽出来通用的)
- 需要抽出来的独立于功能模块的应该是common_util 和 common_widget,分别是通用工具层和通用自定义控件层
具体例子分配如下:
- GloabApp 全局Application
- RootAct 启动页面
- Base目录 基础activity fragment存放
- util目录 通用工具
- mywidget 通用自定义控件
- SampleModule Sample功能模块。里面包含独立的MVP的接口
3.2 Model层
Model层中又可以分为Api层和Cache层。
3.2.1 Api层
主要是网络获取数据信息等接口。
使用了自己二次封装过的Retrofit+Okhttp+Gson组合。详细可以参见文章:http://www.jianshu.com/p/283d1a7a0aff
示例SampleApi:
public class SampleApi extends BaseApi {
private static final String mBaseUrl = "http://192.168.3.1/";
private ApiStore mApiStore;
public SampleApi() {
super(mBaseUrl);
mApiStore = mRetrofit.create(ApiStore.class);
}
/**
* 获取xxx数据
* @param uid
* @param callback
*/
public void getSampleInfo(String uid, ApiCallback callback) {
Call call = ((ApiStore)mApiStore).getSampleInfo(uid);
call.enqueue(new RetrofitCallback(callback));
}
public interface ApiStore {
@FormUrlEncoded
@POST("test_retrofit.php")
Call getSampleInfo(@Field("uid") String uid);
}
}
3.2.2 Cache层
本地缓存部分数据。
使用了ASimpleCache缓存开源代码。详细可以参见文章:http://www.jianshu.com/p/25c107ed7348
示例SampleCache:
public class SampleCache extends BaseCache {
private final String KEY_NEWEST_SAMPLE_INFO = "sample_newest_info";
public SampleCache(Context context) {
super(context);
}
/**
* 保存sample信息
* @param serializable
*/
public void saveNewestSample(Serializable serializable) {
mCache.put(KEY_NEWEST_SAMPLE_INFO, serializable);
}
/**
* 获取sample信息
* @return
*/
public SampleInfo getNewestSampleInfo() {
return (SampleInfo) mCache.getAsObject(KEY_NEWEST_SAMPLE_INFO);
}
/**
* 移除缓存
*/
public void removeNewestSampleInfo() {
mCache.remove(KEY_NEWEST_SAMPLE_INFO);
}
}
3.3 Data层
实体化数据类。
3.4 Presenter层
Presenter层又可以分为Contract协议接口,和具体的Presenter处理
3.4.1 Contract层
负责约定view层和presenter层的接口,view和presenter实现相应接口,最终达到解耦的目的。
SampleContract示例:
public interface SampleContract {
interface View {
void showSample(SampleInfo sampleInfo); //显示sample
void errorGetSample(String msg); //显示错误信息
}
interface Presenter {
void getNewestSample(); //获取当前最新的xxx
}
}
3.4.2 Presenter层
负责从model层获取数据、整理数据、行为处理等。处理后调用view显示数据。
SamplePresenter示例:
public class SamplePresenter extends BasePresenter implements SampleContract.Presenter {
private SampleContract.View mView;
private SampleApi mApi;
private SampleCache mCache;
public SamplePresenter(SampleContract.View view) {
mView = view;
mApi = new SampleApi();
mCache = new SampleCache(GlobalApp.getInstance().getContext());
}
@Override
public void getNewestSample() {
//先从缓存获取
SampleInfo sampleInfo = mCache.getNewestSampleInfo();
if(sampleInfo == null) {
//从网络获取
mApi.getSampleInfo("uid", new BaseApi.ApiCallback() {
@Override
public void onSuccess(GetSampleInfoRet ret) {
//缓存
mCache.saveNewestSample(ret.data);
//页面显示
mView.showSample(ret.data);
}
@Override
public void onError(int err_code, String err_msg) {
//服务端返回错误码
mView.errorGetSample(err_msg);
}
@Override
public void onFailure() {
//网络请求或者解析错误
mView.errorGetSample("服务器请求错误");
}
});
} else {
mView.showSample(sampleInfo);
}
}
}
3.5 view层
即平时所说的activity、fragment等。继承自SampleContract的view接口,只负责UI相关显示刷新等。由于拉出了presenter层,view层的代码变得极为清晰
SampleActivity示例:
public class SampleActivity extends BaseActivity implements SampleContract.View {
@BindView(R.id.txtName)
TextView txtName;
@BindView(R.id.imgAvatar)
ImageView imgAvatar;
private SampleContract.Presenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
ButterKnife.bind(this);
mPresenter = new SamplePresenter(this);
}
@Override
public void showSample(SampleInfo sampleInfo) {
txtName.setText(sampleInfo.sample_name);
Glide.with(this)
.load(sampleInfo.avatar)
.into(imgAvatar);
}
@Override
public void errorGetSample(String msg) {
//错误信息
}
}
4 总结
以上代码Github地址:
https://github.com/tsy12321/BaseAndroidProject
注:该项目会做成一个基础的项目框架,包含各种封装好的工具,底层库和MVP架构,还在不断更新中,欢迎关注提Issue!
结尾
更多文章关注我的公众号