Android MVP架构
Android MVP架构
这是一个Kotlin +Okhttp3+Retrofit2+Rxjava2搭建的MVP初始App架构
希望您在看文章之前对MVP有所了解
1. MVP的三大角色
按照国际惯例先放上图片
从上图可以知道:View和Presenter可以相互持有,只有Presenter可以与model进行交互。
Model(数据层): model提供了数据的存取功能,数据可以来自网络或者本地。提供操作数据的方法给presenter层使用。
View(UI层):View层不需要写具体的逻辑,只关心UI操作,因为逻辑封装在了presenter中,View负责调用presenter的方法,然后执行相关的回调来操作UI。
Presenter(调度层): 连接model和View,进行相关的调度。 您的程序逻辑写在presenter中
2. MVP的架构优缺点
先吹一波。
优点:易于扩展,易于维护,易于测试,耦合度低,复用性高,程序健壮稳定等等
接受现实。
缺点:程序复杂性提高,代码文件变多,要解决内存泄漏问题(下文说明)
3. 动手前的架初始依赖
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.1.2'
//Rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.contrarywind:Android-PickerView:3.2.4'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
4.从登录案例中来用MVP
项目整体代码
解决MVP中的内存泄漏:
由于Presenter经常性的需要执行一些耗时操作,那么当我们在操作未完成时候关闭了Activity,会导致Presenter一直持有Activity的对象,造成内存泄漏。有人可能觉得,我直接在activity中onDestory中解除Presenter和activity的关系不就行了吗?但是并不是所有情况下,onDestory都会被正常调用。
我们需要通过弱引用Activity来解决这个问题
先建立一个BasePresenter
/**
* 弱引用View 防止内存泄漏
* View的类型通过泛型类型传递进来,Presenter对这个View持有弱引用
*/
abstract class BasePresenter<T> {
protected var mViewrRef: Reference<T>? = null
protected val view: T
get() = mViewrRef!!.get()!!
val isAttachView: Boolean
get() = null != mViewrRef && null != mViewrRef!!.get()
/**
* 与View建立关联
*/
fun attachView(view: T) {
mViewrRef = WeakReference(view)
}
/**
* 解除与View的关联
*/
fun detachView() {
if (null != mViewrRef) {
mViewrRef!!.clear()
mViewrRef = null
}
}
}
创建一个BaseActivity基类,通过其生命周期来控制它与Presenter的关系
/**
两个泛型参数:第一个是View的接口类型,第二个是Presenter的具体类型
在oncreate()中会构建一个具体的Presenter,构建presenter后调用attachView与activity建立关联
在onDestory中解除对activity的引用。
这样子即使在onDetory()没有被正常调用的情况下。弱引用对象也能及时回收
*/
abstract class BaseActivity<V, T : BasePresenter<V>> : AppCompatActivity(){
protected var mPresenter: T? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mPresenter = createPresenter()
mPresenter?.attachView(this as V)
}
override fun onDestroy() {
super.onDestroy()
mPresenter?.detachView()
}
//创建一个Presenter(
protected abstract fun createPresenter(): T
//每个子类的初始化方法
protected abstract fun init()
/**
* toast
*/
protected fun Any.toast(context: Context, duration: Int = Toast.LENGTH_SHORT): Toast {
val makeText = Toast.makeText(context, this.toString(), duration)
makeText.setGravity(Gravity.CENTER, 0, 0)
return makeText.apply { show() }
}
}
基类建立完毕后我们就可以用登录案例来模拟实现MVP了。
登录界面如下:
View层:MainActivity主要实现按钮点击登录和View接口的回调,进行一个UI操作
class MainActivity : BaseActivity<MainActivity, LoginPresenter>(), ILoginView{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
}
/**
* 创建你的LoginPresenter
*/
override fun createPresenter(): LoginPresenter {
return LoginPresenter(this)
}
/**
* 初始化方法
*/
override fun init() {
//按钮点击
btnLogin.setOnClickListener {
mPresenter!!.login(edtUserName.text.toString(),edtPassword.text.toString())
}
}
/**
* View的回调方法
* 提示相关信息
*/
override fun showInfoStr(msg: String) {
msg.toast(this)
}
/**
* View的回调方法
* 登录成功回调
*/
override fun loginSuccess(it: User) {
}
}
Presenter层:具体的业务逻辑和连接View和model。这里主要使用了Rxjava
class LoginPresenter(private val loginView: ILoginView) : BasePresenter<MainActivity>() {
private val mainManger = LoginModel()
//LoginInterface
@SuppressLint("CheckResult")
fun login(userName: String, password: String) {
mainManger.login(userName, password)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// if("成功"){
// loginView.loginSuccess(it)
// }else{
// loginView.showInfoStr("错误信息")
// }
}, {
loginView.showInfoStr(it.message!!)
})
}
}
Model层:进行接口API的调用,返回一个Observable 给Presenter进行进一步的处理
/**
* 登录模块的model
*/
class LoginModel: ILoginModel{
override fun login(userName: String, password: String): Observable<User> {
return RetrofitClient.getApi().login(userName, password)
}
}
项目结构相对清晰简单,更多的可以参照项目源码:
GitHub传送门
推荐阅读
-
Android自定义View 使用PathMeasure简单模仿系统ProgressBar(四)
-
Android studio中IDE窗口怎么显示或者隐藏?
-
Android studio怎么使用git获取最新内容然后合并?
-
Android Caused by: java.lang.ClassNotFoundException解决办法
-
Android开发之菜单(menu)用法实例分析
-
Android自定义ScrollView实现放大回弹效果实例代码
-
Android编程之ActionBar Tabs用法实例分析
-
Android自定义View圆形进度条控件(三)
-
Android studio中logcat提示信息设置?
-
Android获取本地相册图片和拍照获取图片的实现方法