Android之MVP(二)之深入封装

转自: 

http://blog.csdn.net/dantestones/article/details/51445208


Android之mvp(一) 之入门使用中我简单的介绍了mvp,以及怎么写mvp。我自己也将mvp运用到了项目中,其实mvp并没有固定的写法,正确的去理解架构的思想,都可以有自己独特的mvp写法。git上也有很多例子,比如google的android-architecture,simple哥的Android 源码设计模式解析与实战中也有mvp的讨论。这里参考了simple哥做了一个通用版的mvp,并对google的MVP做了一点自己的解析。

关于presenter一直持有Activity对象导致的内存泄漏问题

只要用过mvp这个问题可能很多人都知道。写mvp的时候,presenter会持有view,如果presenter有后台异步的长时间的动作,比如网络请求,这时如果返回退出了Activity,后台异步的动作不会立即停止,这里就会有内存泄漏的隐患,所以会在presenter中加入一个销毁view的方法。现在就在之前的项目中做一下修改

//presenter中添加mvpView 置为null的方法
public void onDestroy(){    
    mvpView = null;
}

//退出时销毁持有Activity
@Override
protected void onDestroy() {    
    mvpPresenter.onDestroy();    
    super.onDestroy();
}

presenter中增加了类似的生命周期的方法,用来在退出Activity的时候取消持有Activity。

但是在销毁后需要思考一点,后台的延时操作返回时,这个时候view被销毁了,如果接着去调用view的方法就 会抛出空指针异常。所以在后台的延时操作中需要考虑到这种可能产生空指针的情况,尤其是网络请求。

BasePresenter

如果每一个Activity都需要做绑定和解绑操作就太麻烦了,现在我希望可以有一个通用的presenter来为我们添加view的绑定与销毁。

public abstract class BasePresenter<T> {
    public T mView;
    public void attach(T mView) {
        this.mView = mView;
    }

    public void dettach() {
        mView = null;
    }
}
因为不能限定死传入的View,所以使用泛型来代替传入的对象。通过这个通用的presenter我就可以把原来的 MvpPresenter 简化成下面的样子
public class LoginPersenter extends BasePresenter<IUserLoginView> {
    IUserLoginView loginView;
    LoginModel loginModel;
    private Handler mHandler = new Handler();

    public LoginPersenter(IUserLoginView loginView) {
        this.loginView = loginView;
        loginModel = new LoginModel();
    }

    public void clear() {
        loginView.clearPassword();
        loginView.clearUserName();
    }

    public void login() {
        loginView.showLoading();
        loginModel.login(loginView.getUsername(), loginView.getPassword(), new OnLoginListener() {
            @Override
            public void loginSuccess(final User user) {
                mHandler.post(
                        new Runnable() {
                            @Override
                            public void run() {
                                loginView.hideLoading();
                                loginView.UpdateView(user);
                            }
                        }
                );
            }
            @Override
            public void loginFailed() {
                mHandler.post(
                        new Runnable() {
                            @Override
                            public void run() {
                                loginView.hideLoading();
                                loginView.showFailedError();
                            }
                        }
                );
            }
        });
    }
}

BaseView

界面需要提供的UI方法中会有很多类似的UI方法,可以把它们提取到一个公共的父类接口中。比如提取显示loading界面和隐藏loading界面的方法,其他的view层接口就可以直接继承BaseView接口,不必重复的写显示和隐藏loading界面方法。

public interface BaseView {    
    void showLoading();   
    void hideLoading();
}

BaseMvpActivity

presenter绑定到activity和View的绑定和解绑操作是每个Activity都会去做的,同样这里我也希望能有一个父类来完成这个统一的操作。

public abstract class BaseMvpActivity <V,T extends BasePresenter<V>> extends AppCompatActivity {
    public T presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = initPresenter();
    }

    @Override
    protected void onResume() {
        super.onResume();
        presenter.attach((V)this);
    }

    @Override
    protected void onDestroy() {
        presenter.dettach();
        super.onDestroy();
    }

    // 实例化presenter
    public abstract T initPresenter();
}

同样使用泛型来提取通用的逻辑,presenter的初始化,以及view的绑定和解绑操作都提取到父类Activity中。向外部提供了一个 initPresenter(); 方法用来初始化presenter,如果想创建不同参数的构造函数都可以随意去创建。

更加通用的例子

通过上面的base父类,对之前的例子进行优化,写一个更加好用的例子。

  • IUserLoginView继承BaseView接口,添加自己的初始化方法
public interface IUserLoginView extends BaseView{
    void clearUserName();
    void clearPassword();
    String getUsername();
    String getPassword();
    void UpdateView(User user);
    void showFailedError();
}
  • LoginPresenter 继承BasePresenter类,增加网络请求和处理点击事件的方法
public class LoginPersenter extends BasePresenter<IUserLoginView> {
    IUserLoginView loginView;
    LoginModel loginModel;
    private Handler mHandler = new Handler();

    public LoginPersenter(IUserLoginView loginView) {
        this.loginView = loginView;
        loginModel = new LoginModel();
    }

    public void clear() {
        loginView.clearPassword();
        loginView.clearUserName();
    }

    public void login() {
        loginView.showLoading();
        loginModel.login(loginView.getUsername(), loginView.getPassword(), new OnLoginListener() {
            @Override
            public void loginSuccess(final User user) {
                mHandler.post(
                        new Runnable() {
                            @Override
                            public void run() {
                                loginView.hideLoading();
                                loginView.UpdateView(user);
                            }
                        }
                );
            }
            @Override
            public void loginFailed() {
                mHandler.post(
                        new Runnable() {
                            @Override
                            public void run() {
                                loginView.hideLoading();
                                loginView.showFailedError();
                            }
                        }
                );
            }
        });
    }
}
  • MainActivity
public class MainActivity extends BaseMvpActivity<IUserLoginView, LoginPersenter> implements IUserLoginView {
    private EditText mEtUsername, mEtPassword;
    private Button mBtnLogin, mBtnClear;
    private ProgressBar mPbLoading;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_login);
        initViews();
    }

    private void initViews() {
        mEtUsername = (EditText) findViewById(R.id.id_et_username);
        mEtPassword = (EditText) findViewById(R.id.id_et_password);
        mBtnClear = (Button) findViewById(R.id.id_btn_clear);
        mBtnLogin = (Button) findViewById(R.id.id_btn_login);
        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
        mBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.login();
            }
        });
        mBtnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.clear();
            }
        });
    }

    @Override
    public void showLoading() {
        mPbLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        mPbLoading.setVisibility(View.GONE);
    }

    @Override
    public String getUsername() {
        return mEtUsername.getText().toString();
    }

    @Override
    public String getPassword() {
        return mEtPassword.getText().toString();
    }

    @Override
    public void UpdateView(User user) {
        Toast.makeText(this, "登录成功!!!", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void clearUserName() {
        mEtUsername.setText("");
    }

    @Override
    public void clearPassword() {
        mEtPassword.setText("");
    }

    @Override
    public void showFailedError() {
        Toast.makeText(this, "登录失败!!!", Toast.LENGTH_SHORT).show();
    }

    @Override
    public LoginPersenter initPresenter() {
        return new LoginPersenter(this);
    }
}
最终的成果,我们只需要在Acitivity中传入泛型对象,并写好 initPresenter()  Presenter的初始化的方法就可以直接去使用presenter,当然View的接口还是要自己去实现。 
以上的方法只是一些比较简单的封装。

源码下载:http://download.csdn.net/detail/linder_qzy/9580531





  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值