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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章