天天看點

Android學習筆記之MVP架構初體驗

随着對Android開發的逐漸學習,自己的代碼量也越來越多,功能也越來越複雜,雖然盡量将一些方法封裝或者寫成單獨的根據類,但是一個項目下來自己的代碼還是太雜亂,太臃腫了。而且在多人開發的時候問題更加突出,是以我認為有必要給自己的項目一個合理的架構了,這次要介紹的是MVP模式在Android開發中的應用。

首先介紹一下MVP,其中M全稱呼Model,是整個APP中的資料來源,比如網絡請求類以及資料庫之類的;V指View,當然是指視圖了,在Android中一般為Avtivity及Fragment;P

全稱Presenter,負責處理Model提供的資料,友善View來顯示。這樣一來,整個APP的架構就非常清晰了,三個部分各司其職,這樣友善了代碼的維護和可讀性。

下面我們用MVP架構來實作一個登入功能

首先,我們要建立三個接口,它們是整個工程的MVP接口的父類,是以它隻包含最基本的方法

}
           
public interface MvpPresenter<V extends MvpView> {
    //綁定View
    public void attachView(V view);
    //解除綁定
    public void detachView();
}
           
public interface MvpModel  {
}
           

可以看到,隻有MvpPresenter裡有兩個方法,它們用來綁定和解綁view,至于MvpView和MvpModel接口如果工程沒有什麼能夠抽象出來的方法就可以不寫

接下來為我們的登入子產品建立接口

public interface LoginView<M> extends MvpView {
    //顯示加載中的視圖
    public void showLoading(boolean pullToRefresh);
    //顯示登入人資訊
    public void showInformation(User user);
    //登入失敗提示
    public void loginFail(String err);
}
           

Presenter中需要根據不同的View和不同的Model來編寫邏輯代碼,是以這裡隻使用了一個抽象類來實作

public abstract class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V>{

    private V view;

    @Override
    public void attachView(V view) {
        this.view = view;
        Log.d("@@MVP:","view attached");
    }

    @Override
    public void detachView() {
        if (view !=null){
            view = null;
            Log.d("@@MVP:","view deattached");
        }
    }

}

           
}
           

然後就可以為我們的登入子產品編寫MVP的代碼了,這裡使用了一個User實體類和UserBiz登入工具類,還建立了一個登入狀态的回調接口OnLoginListener和登入方法接口UserLogin

public interface UserLogin {
    public void login(String username, String password, OnLoginListener loginListener);
}
           
public interface OnLoginListener {
    void loginSuccess(User user);

    void loginFailed();
}
           
public class User {
    private String username ;
    private String password ;

    public String getUsername()
    {
        return username;
    }

    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }
}
           
public class UserBiz implements UserLogin {
    @Override
    public void login(final String username, final String password, final OnLoginListener loginListener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (username.equals("327570221") && password.equals("123")) {
                    User user = new User();
                    user.setUsername(username);
                    user.setPassword(password);
                    loginListener.loginSuccess(user);

                }else {
                    loginListener.loginFailed();
                }
            }
        }).start();
    }
}

           
下面是登入子產品的MVP代碼      
Model層僅提供了登入方法      
public class LoginModel extends MvpBaseModel {
    Context mContext;
    public LoginModel(Context mContext){
        this.mContext=mContext;
    }

    public void Login(String name, String psd, OnLoginListener listener){
        UserBiz userBiz=new UserBiz();
        userBiz.login(name,psd,listener);
    }
}
           
Presenter層負責調用Model層的登入方法并且根據結果改變UI狀态
public class LoginPresenter extends MvpBasePresenter {
    private LoginModel loginModel;
    Context mContext;
    LoginView loginView;
    Handler handler;

    public LoginPresenter(Context mContext,LoginView v){
        this.mContext=mContext;
        loginModel=new LoginModel(mContext);
        loginView= v;
        handler=new Handler();
    }


    public void Login(String name, String psd){
       loginView.showLoading(true);
        loginModel.Login(name, psd, new OnLoginListener() {
            @Override
            public void loginSuccess(final User user) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        loginView.showInformation(user);
                        loginView.showLoading(false);
                    }
                });

                Log.d("@@","登入成功");
            }

            @Override
            public void loginFailed() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        loginView.loginFail("登入失敗");
                        loginView.showLoading(false);
                    }
                });

                Log.d("@@","登入失敗");
            }
        });
    }
}
           
BaseActivity在onCreat和onDestory時綁定和解綁Presenter
public abstract class BaseActivity<P extends MvpBasePresenter> extends AppCompatActivity implements MvpView {
    P presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = bindPresenter();
        Log.d("@@","onCreated");
        //我的presenter中介我是不是管理View---關聯
        if (presenter != null){
            Log.d("@@","attachView");
            presenter.attachView(this);

        }
    }

    //具體的實作我不知道,我給别人實作
    public abstract P bindPresenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("@@","onDestory");
        //Activity銷毀的時候---解除綁定
        if (presenter != null){
            Log.d("@@","detachView");
            presenter.detachView();

        }
    }

}

           
View層僅僅實作了改變View的方法,是不是很清爽
public class MvpLoginActivity extends BaseActivity implements LoginView{

    EditText userName;
    EditText passWord;
    Button loginBtn;
    TextView infoTv;
    ProgressBar progressBar;
    LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_login);


        userName= (EditText) findViewById(R.id.editText);
        passWord= (EditText) findViewById(R.id.editText2);
        loginBtn= (Button) findViewById(R.id.login);
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String name=userName.getText().toString();
                String pass=passWord.getText().toString();
                Log.d("@@mvpActivity",name+pass);
                presenter.Login(name,pass);
            }
        });
        infoTv= (TextView) findViewById(R.id.textView);
        progressBar= (ProgressBar) findViewById(R.id.progressBar);
        progressBar.setVisibility(View.GONE);

    }

    @Override
    public MvpBasePresenter bindPresenter() {
        presenter=new LoginPresenter(MvpLoginActivity.this,this);
        return presenter;
    }

    @Override
    public void showLoading(boolean pullToRefresh) {
        if (pullToRefresh){
        progressBar.setVisibility(View.VISIBLE);}
        else {
            progressBar.setVisibility(View.GONE);
        }
    }

    @Override
    public void showInformation(User user) {
        String s=user.getUsername()+"\n"+user.getPassword();
        infoTv.setText(s);
    }

    @Override
    public void loginFail(String err) {
        infoTv.setText(err);
    }
}

           
這樣我們的demo就完成了,需要的可以下載下傳我的demo:點選打開連結