MVP+Dragger2+Rxjava2+Retrofit+OKhttp框架已经流行很长时间,而且也必将成为未来android开发的趋势,在使用这个框架的过程中踩过很多坑,
所以想把我的经验告诉大家,让大家少踩一点弯路。
首先介绍一下MVC和MVP的概念,不懂的同学可以自行百度:
MVC:
- View:对应于布局文件
- Model:业务逻辑和实体模型
- Controllor:对应于Activity
MVP :
- View 对应于Activity,负责View的绘制以及与用户交互
- Model 依然是业务逻辑和实体模型
- Presenter 负责完成View于Model间的交互
最明显的区别就是,MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。
还有一点就是Presenter与View之间的交互是通过接口的(代码中会体现), 也就是说MVP中完成了M层和V层的解耦。
回到本文来,我将介绍怎么把MVP引入到我们的项目中去,
在Android Studio中引入相关配置 在app的build.gradle中添加如下配置:
引入Dragger2 android { compileSdkVersion 25 buildToolsVersion "25.0.0" defaultConfig { applicationId "vko.cn.myapplication" minSdkVersion 19 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" jackOptions { enabled true } // ndk { // // 设置支持的SO库架构 // abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a' // } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } //使用DataBinding dataBinding { enabled = true } // 使用Java1.8, lamdba compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } 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:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' // Rxjava2 + Retrofit + okHttp compile 'io.reactivex.rxjava2:rxjava:2.1.1' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'com.squareup.okhttp3:okhttp:3.8.1' //Dragger2 compile 'com.google.dagger:dagger:2.7' annotationProcessor 'com.google.dagger:dagger-compiler:2.7' provided 'javax.annotation:jsr250-api:1.0' //底部菜单 compile 'com.ashokvarma.android:bottom-navigation-bar:1.3.0' compile 'com.android.support:design:24.0.0'
在Module的build.gradle中引入:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.0' // //Dagger2 // classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //lamdba classpath 'me.tatarka:gradle-retrolambda:3.2.0' // classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.1-2" // classpath "org.jetbrains.kotlin:kotlin-android-extensions:1.0.1-2" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }
然后开始构建项目。 项目结构如下:
本文只是一个简单的登陆功能展示。
首先,我们来看一下M层,V层,P层的接口:如下
/** * Created by A on 2017/9/4. */ public class InterfaceContract { public interface LoginView extends BaseView { String getUserName(); String getPassWord(); void loginSuccess(); void loginFail(); } public interface Presenter<T extends BaseActivity> { void attachView(T t); void detachView(); } public interface ILoginModule{ void login(String userName , String passWord, InterfaceContract.OnLoginListener listener); } /** * presenter层 的回调 */ public interface OnLoginListener { void LoginSucess(); void LoginFail(); } }
LoginView 用来获取用户账号,密码,登陆成功,失败的相关操作 , 继承自BaseView 然后我们再来看看BaseView:
/** * Created by A on 2017/9/4. */ public interface BaseView { void startProgress(); void stopProgress(); }
这是一个公共的显示进度的接口,用来显示,隐藏进度条,本项目的进度条采用自定义,可以再Uti包l中查看,
P层中OnLoginListener接口,是用来处理M层登陆成功,失败的回调,而Persenter接口是所有P层的公共接口,
用来处理正在网络请求时,Activity异常退出的问题。
接口看完了,让我们回到代码中,一起看看BaseAcitiviyt的代码:
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView { @Inject public T presenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { supportRequestWindowFeature(Window.FEATURE_NO_TITLE); //去掉Activity上面的状态栏 getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN , WindowManager.LayoutParams. FLAG_FULLSCREEN); super.onCreate(savedInstanceState); initPresenter(); initView(); if (Build.VERSION.SDK_INT >= 21) { View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(Color.TRANSPARENT); } } public abstract void initPresenter(); public abstract void initView(); @Override public void startProgress() { if (!ProgressDlgUtil.isShowing()){ ProgressDlgUtil.showSuccinctProgress(this,"正在验证中...",2,false,false); } } @Override public void stopProgress() { ProgressDlgUtil.dismiss(); } }很简单,定义一个抽象的方法initPresenter();实现BaseView接口,使用泛型获取Presenter的对象,由实现它的子类做具体实现, 相信很好理解,
然后我们再来看看LoginActivity的部分
public class LoginActivity extends BaseActivity<LoginPresenter> implements InterfaceContract.LoginView, View.OnClickListener { private ActivityLoginBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void initPresenter() { //完成依赖 DaggerLoginCompent.builder() .loginModule(new LoginModule(this)) .build() .inject(this); } @Override public void initView() { binding = DataBindingUtil.setContentView(this, R.layout.activity_login); binding.btLogin.setOnClickListener(this); } @Override protected void onDestroy() { presenter.detachView(); super.onDestroy(); } @Override public String getUserName() { String name = binding.etUser.getText().toString().trim(); return name; } @Override public String getPassWord() { String md5PassWord = MD5Utils.md5(binding.etPwd.getText().toString().trim()).toLowerCase(); return md5PassWord; } @Override public void loginSuccess() { Intent intent = new Intent(this,MainActivity.class); startActivity(intent); finish(); } @Override public void loginFail() { Toast.makeText(this,"登陆失败",Toast.LENGTH_LONG).show(); } @Override public void onClick(View view) { presenter.login(); } }
LoginActivity继承自BaseBactivity 实现了LoginView接口,本项目才用DataBing绑定获取对象(DataBing的绑定可以自行百度,减少FindViewById的操作)。在initPresenter中
通过Dagger2注解,来获取Presenter的对象,
因为内容较多,Dagger2的用法,将在下次讲解,本次只介绍MVP的内容如何应用到项目中。可以看到V层获取到presenter
的对象以后,通过点击事件,调用login()方法,这时逻辑就到了P层,然后我们再来看看LoginPresenter的代码:
public class LoginPresenter extends BasePresenter<LoginActivity> implements InterfaceContract.OnLoginListener{ // private InterfaceContract.LoginView mView; private LoginModelImple modelImple; @Inject public LoginPresenter(InterfaceContract.LoginView mView){ attachView((LoginActivity) mView); modelImple = new LoginModelImple(); } public void login(){ mView.startProgress(); String userword = mView.getUserName(); String passWord = mView.getPassWord(); modelImple.login(userword,passWord,this); } @Override public void LoginSucess() { mView.stopProgress(); mView.loginSuccess(); } @Override public void LoginFail() { mView.stopProgress(); mView.loginFail(); } }同样很简单,思路也很清晰, 通过构造方法获取V层的对象,然后通过传捡来的这个对象,获取user的账号密码,在构造方法中处理化M层的对象,调用M层login()方法,处理登陆的逻辑就到了M层,我们再来看看M层的逻辑,
public class LoginModelImple implements InterfaceContract.ILoginModule{ public Context context; @Override public void login(String userName, String passWord, final InterfaceContract.OnLoginListener listener) { Observable<BaseEntity<UserInfo>> observable = RetrofitFactory.builder().getService().login(userName,passWord); observable.compose(RxSchedulers.compose()).subscribe(new BaseObserver<UserInfo>(VKOApplication.getInstance()) { @Override protected void onHandleSuccess(UserInfo userInfo) { LogUtils.d(this,"登陆请求的回调-------"); LogUtils.d(this,"userInfo.getToken() = " + userInfo.getToken()); SPUtils.put(VKOApplication.getInstance(),"user_token",userInfo.getToken()); // 保存用户信息等操作 listener.LoginSucess(); } }); } }
同样的,M层login方法用来处理登陆请求成功 以及 失败对应的方法,我们可以看到,onHandleSucces方法中,回调了P层的登陆成功的方法(登陆失败已经封装到Obseable中,
有兴趣的可以看看)
然后我们再回到P层中,可以看到到,LgoinSucess()方法中,做了两件事,隐藏进度条,回调V层。 然后交给
V层来处理登陆成功和失败的相关操作,
再来看看LoginAcitivity中,登陆成功跳转MainActivity ,失败则提示用户账号或密码错误, 整个登陆的逻辑就完成了。可以发现在这个过程中,我们的思路
非常清晰,
整个流程采用接口回调的方式,让代码更整洁清除,同时完成了V层和M层的解耦操作。
V层只负责更新UI 而处理耗时操作的逻辑都放在了M层, P层则是连接M和V的桥梁。 好了MVP运用到项目中就到这,下一章我将给大家介绍,如何结合MVP
使用Retrofit+okhttp等android现在流行的框架。
项目地址 :https://github.com/yy-Kevin/MVP-Dagger-Rxjava2-Retrofit