对此题目有兴趣的同学都是知道至少了解这个东西,在此我不准备讲太多的理论,因为互联网上对MVP的介绍铺天盖地了.例如这位同学的文章就很精彩 Android MVP 详解(上),本人只是介绍两种MVP在Android的实现方法。
第一种:MVP在Android中2种实现(1)
第二种: MVP在Android中2种实现(2)
本文是对 MVP在Android中2种实现(1)中讲述的MVP实现方式的一种改进和增强。第一种方法最大的弊端就是有可能造成内存泄漏,本文利用虚引用结合Activity的生命周期来解决这个问题。
第一种方法会造成内存泄漏是由于Presenter持有了View的强引用,导致当系统试图回收销毁的View实例时候受阻导致内存泄漏,那么只要我们让Presenter持有View的弱引用即可解决此问题,下面看代码:
Prestener
我们定义一个Presenter
的基类,让其他presenter继承此类。
package com.ss007.androidmvpsample.enhancedMvp;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
/**
* Copyright (C) 2017 ben
*
*
* @author ben
* @version 1.0
* @createDate 2017/7/5 10:34
* @description
*/
public class BasePresenter<V>
{
protected Reference<V> mViewRef;
public void attachView(V view)
{
mViewRef = new WeakReference<V>(view);
}
public V getView()
{
if (mViewRef == null)
{
return null;
}
return mViewRef.get();
}
public boolean isViewAttached()
{
return mViewRef != null && mViewRef.get() != null;
}
public void detachView()
{
if (mViewRef != null)
{
mViewRef.clear();
mViewRef = null;
}
}
}
这是一个泛型类,参数为V
为要引用的View
.
attachView()
方法的作用是创建View
的虚引用,此方法放在View
初始化过程中,例如onCreate()
方法中detachView()
方法的作用是销毁View
的虚引用,此方法放在View
销毁化过程中,例如onDestroy()
方法中getView()
放用来获取View
View
我们定义一个View
的基类,让其他View继承此类。
package com.ss007.androidmvpsample.enhancedMvp;
import android.os.Bundle;
import com.ss007.androidmvpsample.BaseActivity;
/**
* Copyright (C) 2017
*
*
* @author ben
* @version 1.0
* @createDate 2017/7/5 10:32
* @description
*/
public abstract class MVPBaseActivity<V,T extends BasePresenter<V>> extends BaseActivity
{
protected T mPresenter;
@Override
protected void onCreate(Bundle arg)
{
super.onCreate(arg);
mPresenter=createPresenter();
mPresenter.attachView((V) this);
}
@Override
protected void onDestroy()
{
super.onDestroy();
mPresenter.detachView();
}
protected abstract T createPresenter();
}
这也是一个泛型基类,第一个泛型参数V
:代表当前处理的View,第二个泛型参数T
:代表当前使用的Presenter,要求此presenter
必须继承至
BasePresenter
.要求每个Activity
必须继承至此类。
在onCreate()
和onDestroy()
中的处理逻辑已经在讲解 Prestener 时候已经说过。
createPresenter()
用来创建presenter
实例,便于在View
中通过其调用presenter
的方法,此方法是abstract
的,这就要求MVPBaseActivity
子类必须实现此方法。
讲清楚了这两个类,下面的代码就简单了,基本就是与第一种一样了。
具体实例
第一步:根据业务需要定义两个接口,一个给Presenter实现,一个给View实现。
第二步:如果决定使用轻Model重Presenter的模式,那么Model基本就是一些JavaBean类,让View持有Presenter的引用(最好是以接口声明),那么在View中就可以调用Presenter里面的方法了,例如点击登录按钮,调用presenter里面的login()方法。
第三步:Presenter里面的方法执行后会有结果回来,那么View是要对这些结果做出响应的,例如登录后是成功了还是失败了呢?这就需要从Presenter里面调用View里面的方法将结果传过去。所以需要Presenter持有一个View的引用(最好是以接口声明)。
presenter实现的接口Login2Presenter
package com.ss007.androidmvpsample.enhancedMvp;
/**
* Copyright (C) 2017
*
*
* @author ben
* @version 1.0
* @createDate 2017/7/5 10:40
* @description
*/
public interface Login2Presenter
{
void login(String userName,String passWord);
}
View实现的接口Login2ActView
package com.ss007.androidmvpsample.enhancedMvp;
/**
* Copyright (C) 2017
*
*
* @author ben
* @version 1.0
* @createDate 2017/7/5 10:40
* @description
*/
public interface Login2ActView
{
void loginSuccess(String responseStr);
void loginFailed(String code,String errBody);
}
View
package com.ss007.androidmvpsample.enhancedMvp;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.ss007.androidmvpsample.R;
public class Login2Act extends MVPBaseActivity<Login2Act,Login2PresenterImp> implements Login2ActView
{
private final static String TAG = Login2Act.class.getSimpleName();
private EditText userName, passWord;
private Button btnLogin;
private Activity mAct;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login2);
mAct = this;
userName = (EditText) findViewById(R.id.et_user_name);
passWord = (EditText) findViewById(R.id.et_password);
btnLogin = (Button) findViewById(R.id.btn_login);
btnLogin.setOnClickListener(clickListener);
}
@Override
protected Login2PresenterImp createPresenter()
{
return new Login2PresenterImp();
}
@Override
public void loginSuccess(String responseStr)
{
btnLogin.setText("Login");
Log.d(TAG, String.format("result: %s", responseStr));
Toast.makeText(mAct, "login success", Toast.LENGTH_SHORT).show();
}
@Override
public void loginFailed(String code, String errBody)
{
Log.d(TAG, String.format("error code:%s | error body: %s", code, errBody));
Toast.makeText(mAct, "login failed", Toast.LENGTH_SHORT).show();
}
private View.OnClickListener clickListener = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.btn_login:
if (mPresenter != null)
{
btnLogin.setText("Loginning ...");
mPresenter.login(userName.getText().toString(), passWord.getText().toString());
}
break;
default:
break;
}
}
};
}
Presenter
package com.ss007.androidmvpsample.enhancedMvp;
import android.os.Handler;
/**
* Copyright (C) 2017 ben
*
*
* @author ben
* @version 1.0
* @createDate 2017/7/5 10:40
* @description
*/
public class Login2PresenterImp extends BasePresenter<Login2Act> implements Login2Presenter
{
@Override
public void login(String userName, String passWord)
{
final Login2ActView login2ActView= getView();
//模拟登录(Mock login)
new Handler().postDelayed(new Runnable()
{
@Override
public void run()
{
boolean isSuccess=true;//depend on result from your server
if (login2ActView==null)
return;
if (isSuccess)
{
login2ActView.loginSuccess("the result get from your server");
}
else
{
login2ActView.loginFailed("error code","error body");
}
}
}, 2000);
}
}
小结
Activity
可以这么做,那么Fragment
也可以使用类似的方法处理.
最后祝大家编码快乐,生活快乐。