AndroidフレームワークのMVPモード(例としてログインを取り上げます)

    Androidフレームワークには、通常、MVCモード、MVPモード、およびMVVMモードがあります。最初に概念を明確にしましょう。パターンはコードを編成する構造的な方法を指し、パターンはコード実行の効率を向上させません。このモードは、後続の関数拡張とコードの明確な構造の便宜のために使用されます。

    前回の記事では、最初からAndroid MVCモデルを進化させるためにアクティビティでコードを記述していましたが、最終的には、アクティビティのコードが少し複雑であると感じるかもしれません。これは、 V層とC層はすべてアクティビティによって支えられています。さらに進みたい場合は、MVPモードをさらに理解する必要があります。 

MVPモードは、ソフトウェアをモデル、ビュー、プレゼンターの3つの基本部分に分割します(当面はプレゼンターに変換します)。

    モデル-データの生成に使用されます(ネットワーク、データベースなどへのアクセス)

    ビュー(表示)-レイヤー関連のビュー(インターフェイスレイアウトコントロールおよびその他の関連コード:アクティビティ、フラグメント)

    プレゼンター-MとVを接続する(ビジネスロジックに関連するコード)

MVCモードでは、アクティビティはVレイヤーとCレイヤーの両方の役割を果たします。つまり、アクティビティ内のレイアウトコントロールやビジネスロジック関連コードなどの関連コードに加えて、すべてのビジネスロジック関連コードを抽出します。アクティビティ脱退はMVPモードになります。MVPモードでは、アクティビティにはインターフェイスレイアウトコントロールに関連するコードのみが含まれ、ビジネスロジックに関連するコードは含まれなくなります。アクティビティはビューレイヤーの役割のみを引き受けます。さらに、モデルレイヤーとビューレイヤーは直接相互作用しませんが、プレゼンターは仲介者として機能し、プレゼンターはモデルオブジェクトとビューオブジェクトを保持します。

1.ビジネスロジックの関連コードを抽出し、MVPモードを実現します

最初にプロジェクトでプレゼンターパッケージを作成し、次にLoginPresenter.javaクラスを作成し、アクティビティでビジネスロジック関連のコードを抽出して、ログインのプレゼンターレイヤーを形成します。

//登录模块的业务逻辑
public class LoginPresenter {
    private static final String TAG = "LoginPresenter";

    private LoginActivity loginActivity;

    public LoginPresenter(LoginActivity loginActivity) {
        this.loginActivity = loginActivity;
    }

    public void login(String phoneNumber, String password) {
        //本地对输入情况做校验
        boolean validateOk = validateInput(phoneNumber, password);
        if (validateOk) {
            loginActivity.showProgressBar();
            String md5Password = MD5Utils.getMd5(password);

            LoginModel loginModel = new LoginModel();
            loginModel.gotoLogin(phoneNumber, md5Password, new LoginModel.OnNetResponseListener() {
                @Override
                public void onNetResposeError(String msg) {
                    loginActivity.hideProgressBar();
                    loginActivity.showToast(msg);
                }

                @Override
                public void onNetReponseSuccess(LoginData loginData) {
                    loginActivity.hideProgressBar();
                    switch (loginData.status) {
                        case 200: //用户名未注册
                        case 201: //密码有误
                        case 203: //登录失败
                            loginActivity.showToast(loginData.message);
                            Log.i(TAG, "onResponse: = " + loginData.message);
                            break;
                        case 202:   //登录成功
                            loginActivity.showToast(loginData.message);
                            Log.i(TAG, "onResponse: = " + loginData.message);

                            //本地保存必要的用户信息
                            //......
                            loginActivity.jumpSuccessActivity(loginData);
                            break;
                        default:
                            loginActivity.showToast("登录出现未知异常");
                            break;
                    }
                }
            });
        }
    }

    private boolean validateInput(String phoneNumber, String password) {
        if (TextUtils.isEmpty(phoneNumber)) {
            loginActivity.showToast("手机号不能为空");
            return false;
        }
        if (TextUtils.isEmpty(password)) {
            loginActivity.showToast("密码不能为空");
            return false;
        }
        if (!phoneNumber.matches(Constants.STR_PHONE_REGEX2)) {  //匹配正则表达式
            loginActivity.showToast("请输入正确的手机号");
            return false;
        }
        return true;
    }
}

この時点で、アクティビティにはインターフェイスレイアウトコントロールなどの関連コードのみが含まれています。つまり、ビューレイヤーは次のようになります。

public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "LoginActivity";

    private EditText    etPhoneNumber;
    private EditText    etPassword;
    private Button      btnLogin;
    private ProgressBar progressBar;

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

        etPhoneNumber = (EditText) findViewById(R.id.et_phone_number);
        etPassword = (EditText) findViewById(R.id.et_password);
        btnLogin = (Button) findViewById(R.id.btn_login);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);

        btnLogin.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_login:    //登录
                String phoneNumber = etPhoneNumber.getText().toString().trim();
                String password = etPassword.getText().toString().trim();
                //login(phoneNumber, password);
                LoginPresenter loginPresenter = new LoginPresenter(this);
                loginPresenter.login(phoneNumber, password);
                break;
            default:
                break;
        }
    }

    public void showProgressBar(){
        progressBar.setVisibility(View.VISIBLE);
    }

    public void hideProgressBar(){
        progressBar.setVisibility(View.GONE);
    }
    
    public void showToast(String toast){
        Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
    }

    public void jumpSuccessActivity(LoginData loginData){
        Intent intent = new Intent(LoginActivity.this, LoginSuccessActivity.class);
        startActivity(intent);
        //登录页面直接消失
        finish();
    }
}

モデルレイヤーは元のレイヤーと同じですが、変更はありません。

//Model层只用来产生数据的
public class LoginModel {
    private static final String TAG = "LoginModel";

    public void gotoLogin(String phoneNumber, String md5Password, final OnNetResponseListener listener) {
        OkHttpUtils
                .post()
                .url(Constants.URL_LOGIN)
                .addParams("phoneNumber", phoneNumber)
                .addParams("password", md5Password)
                .build()
                .execute(new StringCallback() {
                    @Override
                    public void onError(okhttp3.Call call, Exception e, int id) {
                        Log.i(TAG, "onError: ---网络访问出现异常---" + e.getMessage());
                        e.printStackTrace();
                        listener.onNetResposeError("网络访问出现异常");
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        Log.i(TAG, "onResponse: 登录成功 response = " + response + " ---");
                        Gson gson = new Gson();
                        LoginData loginData = gson.fromJson(response, LoginData.class);
                        listener.onNetReponseSuccess(loginData);
                    }
                });
    }

    public interface OnNetResponseListener {

        void onNetResposeError(String msg);

        void onNetReponseSuccess(LoginData loginData);
    }
}

現時点では、LoginActivityは3つの独立したM、V、およびPレイヤーに分割されています。モデルレイヤーはデータの生成にのみ使用され、ビューレイヤー、つまりアクティビティにはインターフェイスレイアウトコントロールなどの関連コードのみが含まれます。プレゼンター層は、関連するビジネスロジックの処理を担当します。また、モデルレイヤーとビューレイヤーは直接相互作用しません。

第二に、プレゼンターレイヤーの適用範囲の拡張

    上記の例では、LoginActivityをViewレイヤーとして使用しています。ログインページがLoginFragmentなどのフラグメントになる場合は、Viewレイヤーのコードを変更するだけでなく、Presenterレイヤーのコードも変更する必要があります。上記のLoginPresenterが保持するViewレイヤーオブジェクトはLoginActivityであるため、明らかに不適切です。Presenterレイヤーはビジネスロジックの処理を担当し、ViewレイヤーがActivityであるかFragmentであるかを気にしないためです。Viewレイヤーでの変更Presenterレイヤーのコードに影響を与えないようにする必要があります。Presenterレイヤーのコードは比較的独立している必要があります。

このとき、ビューレイヤー、つまりインターフェースのルール(標準)を定義する必要があり ます。プレゼンターレイヤーでサポートされているビューレイヤーオブジェクトはインターフェースであり、プレゼンターレイヤーは特定のことに注意を払う必要はありません。このインターフェースの実装。ログインページに関連するUI操作を標準化するために、新しいビューパッケージを作成し、ILoginViewインターフェイスを作成します。

public interface ILoginView {

    public void showProgressBar();

    public void hideProgressBar();

    public void showToast(String toast);

    public void jumpSuccessActivity(LoginData loginData);
}

次に、上記のLoginPresenterが保持するViewレイヤー参照をILoginViewインターフェイスに変更します。

//登录模块的业务逻辑
public class LoginPresenter {
    private static final String TAG = "LoginPresenter";

    private ILoginView loginView;

    public LoginPresenter(ILoginView loginView) {
        this.loginView = loginView;
    }

    public void login(String phoneNumber, String password) {
        //本地对输入情况做校验
        boolean validateOk = validateInput(phoneNumber, password);
        if (validateOk) {
            loginView.showProgressBar();
            String md5Password = MD5Utils.getMd5(password);

            LoginModel loginModel = new LoginModel();
            loginModel.gotoLogin(phoneNumber, md5Password, new LoginModel.OnNetResponseListener() {
                @Override
                public void onNetResposeError(String msg) {
                    loginView.hideProgressBar();
                    loginView.showToast(msg);
                }

                @Override
                public void onNetReponseSuccess(LoginData loginData) {
                    loginView.hideProgressBar();
                    switch (loginData.status) {
                        case 200: //用户名未注册
                        case 201: //密码有误
                        case 203: //登录失败
                            loginView.showToast(loginData.message);
                            Log.i(TAG, "onResponse: = " + loginData.message);
                            break;
                        case 202:   //登录成功
                            loginView.showToast(loginData.message);
                            Log.i(TAG, "onResponse: = " + loginData.message);

                            //本地保存必要的用户信息
                            //......
                            loginView.jumpSuccessActivity(loginData);
                            break;
                        default:
                            loginView.showToast("登录出现未知异常");
                            break;
                    }
                }
            });
        }
    }

    private boolean validateInput(String phoneNumber, String password) {
        if (TextUtils.isEmpty(phoneNumber)) {
            loginView.showToast("手机号不能为空");
            return false;
        }
        if (TextUtils.isEmpty(password)) {
            loginView.showToast("密码不能为空");
            return false;
        }
        if (!phoneNumber.matches(Constants.STR_PHONE_REGEX2)) {  //匹配正则表达式
            loginView.showToast("请输入正确的手机号");
            return false;
        }
        return true;
    }
}

現時点では、LoginPresenterは、ビューレイヤーの特定の実装がアクティビティであるかフラグメントであるかを気にする必要はありません。ILoginViewインターフェイスを実装して抽象メソッドを書き直すだけで済みます。これにより、プレゼンターレイヤーの適用性が大幅に向上します。この考えに従っても、Presenterのインターフェイスを作成し、LoginPresenterにこのインターフェイスを実装させることができます。

最終的なパッケージ構造は次のようになります。

上記は、例としてログインすることで実現されるMVPモードです。

おすすめ

転載: blog.csdn.net/beita08/article/details/82725896