前言:如果童鞋对于接口回调,多态,泛型(这个很重要)不是特别熟练,或者不是特别了解,建议还是不要使用这种模式。先谢谢常规MVP练练手,等真正对这些知识能熟练掌握了再来学习MVP模式。这个架构用到了大量的接口,泛型。(基础很重要啊)
使用MVP模式架构项目也有2个了。最新的项目原本想结合Dagger2去做(听说会让结构更加清晰)。不过在看了一上午Dagger2以后,我决定下个项目再集成,先把现有的MVP模式自己封装好。
先说下项目使用技术 RxJava+Retrofit+Gson+butterknife SDK:25 开发环境Studio
如果没有听说过以上技术,自行百度,这些在16年就比较流行了。
首先Gradle配置下
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.0'
testCompile 'junit:junit:4.12'
//网络解析
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
//RxJava相关
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
//控制台输出管理 这个东西是github项目,如果使用需要在Project的gradle下添加下远程仓库的支持 代码如下
compile 'com.github.blindmonk:Library:1.0.3'
//作者自己的jar包,是用于处理沉浸式状态栏的,没有混淆,随便用
compile files('libs/translucent_bars.jar')
//注解框架
compile 'com.jakewharton:butterknife:7.0.0'
如果想添加控制台输出依赖 ,在Progect的gradle下添加如下代码(主要为了解决 原生Log输出字符数限制)
allprojects {
repositories {
jcenter()
//控制台无限制输出 远程仓库
maven { url "https://jitpack.io" }
}
}
基本配置完成,说说MVP模式:
传统的MVC模式,Activity同时担任了View的职责和Controller的职责。整个类过于臃肿,一个8000行的类,让你去维护,有没有砸电脑的心情。
而MVP模式,高度解耦,使得Activity和Fragment只负责View层。
P对应的是Presenter
去年,我所遇到的关于MVP的Demo,接口过于臃肿,并且BaseActivity和BaseFragment封装的不是太好,用到具体Presenter的时候总是伴随着强转,并且需要定义的接口类太多(我相信有过那段体验的童鞋会跟我有同感,我们是外包哎,公司可不管理代码质量怎么样,只要功能有了,程序不闪退就行,也不会因为你代码质量提升了就给我多的项目时间,吐槽下。)
首先,一个好的项目,应当有一个清晰可见的命名规范,否则维护起来,难度大大增加。说说我自己的项目规范吧(万一成为行业规范那?嘿嘿)
在项目 视图包下(个人习惯取名ui )下新建mvp包,mvp包分别有三个子包:iml,interfaces,presenter。
先说说分别是干啥的吧。
iml用于存放各种交互,通常为网络请求。
interfaces用于存放IBaseView以及其子类 这个IBaseView是干嘛的? 就是一个接口,所有的Activity都要实现它或者它的子类。里面封装了各种回调,比如获取到网络数据了,你要有一个对象回调到Activity中去做显示,此时,就需要IBaseView了,本质就是接口。是链接module和view的一个桥梁
presenter:里面存放所有presenter。这个Presenter是干嘛的?就是Activity大部分逻辑处理,为什么说不是全部?因为有的是处理不了的,就比如onActivityResult 里面处理的,就放在Activity中比较合理,放到Presenter就显得不是很合理。
首先定义基类 基类有三个,分别是BasePresenter IBaseView IViewBase
先简单介绍一下:
BasePresenter:所有的Presenter的基类,里面封装了一些通用的东西,如Presenter与Activity绑定,解绑,IBaseView对象的获取等等。可拓展。
IBaseView:是一个接口,主要用于每个Activity的回调处理,其子类也是改接口,需要手动实现。
IViewBase:是一个接口,挂载Presenter 等方法
下面分别贴出代码
BasePresenter
public abstract class BasePresenter<T extends IBaseView> {
public T mView;//这个是要回调的接口的对象
public Context mContext;
protected String token="token";
public void attach(T mView) {
this.mView = mView;
}
public void detachView() {
if (mView != null) {
mView = null;
}
}
}
IBaseView:空接口,用于封装使用
public interface IBaseView {
}
IViewBase: 抽取一些方法,用于在BaseActivity和BaseFragment里去实现
public interface IViewBase<K extends IBaseView,T extends BasePresenter<K>> {
T getPresenter();//Presenter对象
int getLayoutId();//布局id
View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);//创建View
void bindView(Bundle savedInstanceState);//onCreate()执行
View getView();//获取根View
}
下面贴出BaseActivity:
public abstract class BaseActivity<K extends IBaseView,T extends BasePresenter<K>> extends AppCompatActivity implements IViewBase<K,T> ,IBaseView{
/**
* 根文件
*/
protected View rootView;
public T mPresenter;
protected MyTitleBarView mTitleBarView;
protected Context context;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context=this;
init(savedInstanceState);
}
private void init(Bundle savedInstanceState) {
//状态栏设置 true:是否开启 小米和魅族 状态栏字体颜色变色 true:开启 false:不开启 这个是作者的一个jar的代码,用于实现沉浸式状态栏,集成的时候删掉此代码就行,我也会把这个jar上传上去。
StatusUtils.builder(this,R.color.main_color,true);
//初始化RootView
rootView=createView(null,null,savedInstanceState);
//设置布局
setContentView(rootView);
mTitleBarView= (MyTitleBarView) findViewById(R.id.title_bar);
mPresenter=getPresenter();
if (mPresenter != null) {
mPresenter.attach((K) this);
mPresenter.mContext=this;
}
bindView(savedInstanceState);
AppManager.getAppManager().addActivity(this);
getIntentDate(getIntent());
}
@Override
public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view=LayoutInflater.from(this).inflate(getLayoutId(),null);
//项目集成 ButterKnife 如果没用到这个注解插件,注释此行代码,用findViewById就行
ButterKnife.bind(this, view);
return view;
}
protected void back() {
finish();
}
//获取页面传递数据
protected void getIntentDate(Intent intent) {
}
@Override
protected void onDestroy() {
super.onDestroy();
//作者自己的Activity管理类,注释就行,如有兴趣,会上传项目代码,里面都有
AppManager.getAppManager().removeActivity(this);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
back();
}
return false;
}
@Override
public View getView() {
return rootView;
}
上传一个示例Activity:MainActivityCallView 是一个接口,所有的页面回调工作由他完成 集成自IBaseView 接口
MainActivityPresenter 具体的Presenter
public class MainActivity extends BaseActivity<MainActivityCallView,MainActivityPresenter> implements MainActivityCallView{
@Override
public MainActivityPresenter getPresenter() {
return new MainActivityPresenter();
}
@Override
public int getLayoutId() {
return R.layout.activity_main;
}
@Override
public void bindView(Bundle savedInstanceState) {
mPresenter.login();
}
//控制台输出下结果
@Override
public void loginSuccess() {
LogUtils.i("回调成功");
}
}
MainActivityCallView:是一个接口 里面有所有Presenter需要回调到Activity层展示的方法
public interface MainActivityCallView extends IBaseView {
void loginSuccess();
}
MainActivityPresenter:IML_MainAService 是网络交互的接口,用于网络交互,此处随便写了一个。
public class MainActivityPresenter extends BasePresenter<MainActivityCallView>{
private IML_MainAService mService=new IML_MainAService() {
@Override
public void api_login() {
mView.loginSuccess();
}
};
public void login() {
mService.api_login();
}
}
IML_MainAService:代码如下,就是随便写了几个方法,是那么个意思。
public interface IML_MainAService {
void api_login();
}
最后,很重要的一点:命名规范,一个规范的命名对于项目是很重要的,不管这个项目是不是你去维护。有人习惯用缩写,我个人认为能不用缩写就不用,每人会猜你这是干嘛的,尽量用全拼(英语)。
说说这个项目的mvp命名规范:
功能 命名规范
Presenter 带Activity的名称 比如是MainActivity的Presenter 就要写MainActivityPresenter
网络交互 在Presenter中与远程服务器交互的接口 都以IML_开头后跟具体哪个类的全拼,此处作者有自己的标准 Activity和Fragment都取首字母 比如 IML_MainAService 则是 MainActivity的Presenter中网络交互接口
IBaseView 所有的IBaseView子类都应当以主类功能命名,并且后缀固定 作者使用的是CallView结尾,当然也可以使用CallBack结尾,清晰易懂,回调处理比如 :MainActivityCallView 就是主界面的回调接口类
好了MVP模式就说到这里,稍后会上传各种资源。