學習AndroidMVP以及優化

什麼是MVP

M:Model 數據層(網絡,數據庫,文件存儲等等…)
V:View UI層 View、Activity、Fragment/以及他們的子類
P:Presenter 中介(作用:將M層與V層進行關聯,交互的中介)
下面實現一個登陸功能

MVP基本案例

  • 實例代碼
    項目列表
    在這裏插入圖片描述
    HttpUtils
/**
 * @作者: hyc
 * @時間: 2019/12/2
 * @說明:模擬網絡請求
 **/
public class HttpUtils {
    HttpResultListener resultListener;
    public HttpUtils(String userName, int password, HttpResultListener resultListener) {
        this.resultListener = resultListener;
        if (userName.equals("10000") && password == 123456) {
            resultListener.onResult("用戶:10000   登錄成功!");
        } else {
            resultListener.onResult("用戶:10000   登錄失敗");
        }
    }
    public interface HttpResultListener {
        void onResult(String result);
    }
}

LoginModel

/**
 * @作者: hyc
 * @時間: 2019/12/2
 * @說明:M層,網絡請求
 **/
public class LoginModel1 {
    public void login(String userName, int password, final HttpUtils.HttpResultListener httpResultListener) {
        HttpUtils httpUtils = new HttpUtils(userName, password, new HttpUtils.HttpResultListener() {
            @Override
            public void onResult(String result) {
                httpResultListener.onResult(result);
            }
        });
    }
}

LoginView

public interface LoginView1 {
    void loginResult(String result);
}

LoginPresenter

/**
 * @作者: hyc
 * @時間: 2019/12/2
 * @說明:與M、V交互的中介
 **/
public class LoginPresenter1 {
    LoginModel1 loginModel;
    LoginView1 loginView;
    public LoginPresenter(LoginView1 loginView) {
        this.loginModel = new LoginModel();
        this.loginView = loginView;
    }
    public void login(String userName, int password) {
        loginModel.login(userName, password, new HttpUtils.HttpResultListener() {
            @Override
            public void onResult(String result) {
                if (loginView != null) {
                    loginView.loginResult(result);
                }
            }
        });
    }
}

MainActivity

public class MainActivity extends AppCompatActivity implements LoginView1 {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LoginPresenter1 loginPresenter = new LoginPresenter1(MainActivity.this);
                loginPresenter.login("10000",123456);
            }
        });
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
}

R.layout.activity_main

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/btn_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="登錄請求"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        android:textSize="15sp"
        android:layout_marginTop="10dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn_login"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

運行項目點擊登錄
在這裏插入圖片描述

  • 介紹
    // MVP實現 -> 簡單案例 -> 分層次設計 ->基本實現
    // 團隊開發
    // V層 -> MainActivity -> LoginView
    // M層 -> 數據層(網絡請求、數據庫、文件等…)-> LoginModel
    // P層 ->需要新建 -> LoginPresenter
    // 總結:缺點(類結構複雜,接口多),優點(結構清晰,維護性強,有利於團隊開發,模塊維護,功能擴展,降低開發成本)
    ** // 特別適合大型項目,團隊開發**

優化第一步

分析問題:當Activity關閉的時候,而數據請求依然在進行,需要解除UI層與數據層的關聯
解決方案:方法綁定、解綁
attachView ->綁定 、detachView -> 解綁
把之前創建的LoginModel1、LoginPresenter1、LoginView1複製到另一個包下。
項目列表如下在這裏插入圖片描述
修改LoginPresenter2

public class LoginPresenter2 {
    LoginModel2 loginModel;
    LoginView2 loginView;
    public LoginPresenter2() {
        this.loginModel = new LoginModel2();
    }
    //綁定
    public void attachView(LoginView2 loginView) {
        this.loginView = loginView;
    }
    //解綁
    public void detachView() {
        this.loginView = null;
    }
    public void login(String userName, int password) {
        loginModel.login(userName, password, new HttpUtils.HttpResultListener() {
            @Override
            public void onResult(String result) {
                if (loginView != null) {
                    loginView.loginResult(result);
                }
            }
        });
    }
}

MainActivity修改

public class MainActivity extends AppCompatActivity implements LoginView2 {
    private LoginPresenter2 loginPresenter2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter2 = new LoginPresenter2();
                loginPresenter2.attachView(MainActivity.this);
                loginPresenter2.login("10000", 123456);
            }
        });
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenter2.detachView();
    }
}

運行項目,結果一樣。

優化第二步

分析問題:
現在寫一個功能,沒啥問題,如果功能多了,綁定和解除綁定會很煩,爲了統一管理綁定
解決方法:抽象類(把公共抽取出來) -> BasePresenter
重複上一步複製操作
項目列表如下
在這裏插入圖片描述
新建BasePresenter

public abstract class BasePresenter {
    private LoginView3 loginView;
    //綁定
    public void attachView(LoginView3 loginView) {
        this.loginView = loginView;
    }
    //解綁
    public void detachView() {
        this.loginView = null;
    }
    public LoginView3 getLoginView() {
        return loginView;
    }
}

修改LoginPresenter

public class LoginPresenter3 extends BasePresenter {
    LoginModel3 loginModel;
    public LoginPresenter3() {
        this.loginModel = new LoginModel3();
    }
    public void login(String userName, int password) {
        loginModel.login(userName, password, new HttpUtils.HttpResultListener() {
            @Override
            public void onResult(String result) {
                if (getLoginView() != null) {
                    getLoginView().loginResult(result);
                }
            }
        });
    }
}

再修改MainActivity

public class MainActivity extends AppCompatActivity implements LoginView3 {
    private LoginPresenter3 loginPresenter3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter3 = new LoginPresenter3();
                loginPresenter3.attachView(MainActivity.this);
                loginPresenter3.login("10000", 123456);
            }
        });
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenter3.detachView();
    }
}

運行項目,結果一樣

優化第三步

分析問題:BasePresenter抽象類寫死了,只有LoginView,可能還會有登錄或者註冊,或者獲取驗證碼,
希望能動態抽象

解決方案:BaseView解決
重複複製操作
項目列表如下
在這裏插入圖片描述
新建BaseView

public interface BaseView {
}

修改BasePresenter

public abstract class BasePresenter4 {
    private BaseView loginView;
    //綁定
    public void attachView(BaseView loginView) {
        this.loginView = loginView;
    }
    //解綁
    public void detachView() {
        this.loginView = null;
    }
    public BaseView getLoginView() {
        return loginView;
    }
}

修改LoginView4

public interface LoginView4 extends BaseView {
    void loginResult(String result);
}

修改LoginPresenter4

public class LoginPresenter4 extends BasePresenter4 {
    LoginModel4 loginModel;
    public LoginPresenter4() {
        this.loginModel = new LoginModel4();
    }
    public void login(String userName, int password) {
        loginModel.login(userName, password, new HttpUtils.HttpResultListener() {
            @Override
            public void onResult(String result) {
                if (getLoginView() != null) {
                    //這裏強轉
                    LoginView4 view= (LoginView4) getLoginView();
                    view.loginResult(result);
                }
            }
        });
    }
}

修改MainActivity

public class MainActivity extends AppCompatActivity implements LoginView4 {
    private LoginPresenter4 loginPresenter4;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter4 = new LoginPresenter4();
                loginPresenter4.attachView(MainActivity.this);
                loginPresenter4.login("10000", 123456);
            }
        });
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenter4.detachView();
    }
}

運行項目,結果一樣

優化第四步

分析問題:每次都需要強制類型轉換。
解決方案:泛型設計
重複複製操作
項目列表如下
在這裏插入圖片描述
修改BasePresenter5

public abstract class BasePresenter5 <V extends BaseView5>{
    private V loginView;
    //綁定
    public void attachView(V loginView) {
        this.loginView = loginView;
    }
    //解綁
    public void detachView() {
        this.loginView = null;
    }
    public V getLoginView() {
        return loginView;
    }
}

修改LoginPresenter5

public class LoginPresenter5 extends BasePresenter5<LoginView5> {
    LoginModel5 loginModel;
    public LoginPresenter5() {
        this.loginModel = new LoginModel5();
    }
    public void login(String userName, int password) {
        loginModel.login(userName, password, new HttpUtils.HttpResultListener() {
            @Override
            public void onResult(String result) {
                if (getLoginView() != null) {
                    getLoginView().loginResult(result);
                }
            }
        });
    }
}

修改MainActivity

public class MainActivity extends AppCompatActivity implements LoginView5 {
    private LoginPresenter5 loginPresenter5;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter5 = new LoginPresenter5();
                loginPresenter5.attachView(MainActivity.this);
                loginPresenter5.login("10000", 123456);
            }
        });
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenter5.detachView();
    }
}

運行項目,結果一樣

優化第五步

分析問題:一個Activity需要去綁定與解除綁定,如果Activty和Fragment多了,很多代碼冗餘
解決方案:抽象類 ->抽象出綁定與解除綁定 ->BaseActivity
重複複製操作
項目列表如下
在這裏插入圖片描述
新建BaseActivity

public abstract class BaseActivity extends AppCompatActivity {
    private LoginPresenter6 presenter6;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }
    private void init() {
        presenter6 = createPresenter();
        presenter6.attachView(createView());
    }
    public LoginPresenter6 getPresenter6() {
        return presenter6;
    }
    public abstract LoginPresenter6 createPresenter();
    public abstract LoginView6 createView();
    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter6.detachView();
    }
}

修改MainActivity

public class MainActivity extends BaseActivity implements LoginView6 {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
             getPresenter6().login("10000", 123456);
            }
        });
    }
    @Override
    public LoginPresenter6 createPresenter() {
        return new LoginPresenter6();
    }
    @Override
    public LoginView6 createView() {
        return this;
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
}

運行項目,結果一樣

優化第六步

分析問題:BaseActivity還是寫死了,未滿足要求,只能用LoginPresenter6
解決方案:BaseActivity中抽象實現(BasePresenter和BaseView)

重複複製操作
項目列表如下
在這裏插入圖片描述
修改BaseActivity7

public abstract class BaseActivity7 extends AppCompatActivity {
    private BasePresenter7 presenter7;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }
    private void init() {
        presenter7 = createPresenter();
        presenter7.attachView(createView());
    }
    public BasePresenter7 getPresenter7() {
        return presenter7;
    }
    public abstract BasePresenter7 createPresenter();
    public abstract BaseView7 createView();
    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter7.detachView();
    }
}

修改MainActivity

public class MainActivity extends BaseActivity7 implements LoginView7 {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LoginPresenter7 presenter7 = (LoginPresenter7) getPresenter7();
                presenter7.login("10000", 123456);
            }
        });
    }
    @Override
    public LoginPresenter7 createPresenter() {
        return new LoginPresenter7();
    }
    @Override
    public LoginView7 createView() {
        return this;
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
}

運行項目,結果一樣

優化第七步

分析問題:還需要強制類型轉換
解決方案:泛型設計
重複複製操作
項目列表如下
在這裏插入圖片描述
修改BaseActivity8

public abstract class BaseActivity8<V extends BaseView8, P extends BasePresenter8<V>> extends AppCompatActivity {
    private P presenter8;
    private V view8;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }
    private void init() {
        presenter8 = createPresenter();
        if (presenter8 == null) {
            throw new NullPointerException("presenter8 is null");
        }
        view8 = createView();
        if (view8 == null) {
            throw new NullPointerException("view8 is null");
        }
        presenter8.attachView(view8);
    }
    public P getPresenter8() {
        return presenter8;
    }
    public abstract P createPresenter();
    public abstract V createView();
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter8 != null) {
            presenter8.detachView();
        }
    }
}

修改MainActivity

public class MainActivity extends BaseActivity8<LoginView8, LoginPresenter8> implements LoginView8 {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            getPresenter8().login("10000", 123456);
            }
        });
    }
    @Override
    public LoginPresenter8 createPresenter() {
        return new LoginPresenter8();
    }
    @Override
    public LoginView8 createView() {
        return this;
    }
    @Override
    public void loginResult(String result) {
        TextView textView = findViewById(R.id.tv_result);
        textView.setText(result);
    }
}

運行項目,結果一樣

結言

我也是學習別人的自己總結記錄下來,方便以後查看,俗話說,好記性不如爛筆頭,文章寫得不是很好,不喜勿噴,謝謝,有錯誤的地方,歡迎留言
源碼入口github如果有幫助請幫我start 感謝!。

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