Платформа Android теперь обычно имеет режим MVC, режим MVP и режим MVVM. Давайте сначала проясним концепцию: шаблон относится к структурному способу организации кода, а шаблон не улучшает эффективность выполнения кода. Режим используется для удобства последующего расширения функций и понятной структуры кода.
В предыдущей статье с самого начала код был написан в Activity для развития модели Android MVC, но в конце мы все еще можем почувствовать, что код в Activity немного сложен, это связано с ролями слой V и слой C Все относятся к Activity, если мы хотим пойти дальше, нам нужно глубже понять режим MVP.
Режим MVP делит программное обеспечение на три основные части: модель, представление и презентатор (пока я переведу его на презентер).
Модель - используется для генерации данных (доступ к сети, базе данных и т. Д.)
View (Просмотр) -просмотр связанного слоя (элементы управления макетом интерфейса и другие связанные коды: Activity, Fragment)
Докладчик, соединяющий M и V (код, связанный с бизнес-логикой)
В режиме MVC Activity играет роль как уровня V, так и уровня C, то есть, помимо связанного кода, такого как элементы управления макетом и связанный с бизнес-логикой код в Activity, мы извлекаем весь связанный с бизнес-логикой код в Выход из Activity становится режимом MVP. В режиме MVP Activity содержит только код, связанный с элементами управления макетом интерфейса, и больше не содержит кода, связанного с бизнес-логикой. Activity принимает только роль уровня представления. Кроме того, слой модели и слой представления не взаимодействуют напрямую, но презентатор действует как посредник, а презентатор будет удерживать объекты модели и представления.
1. Извлеките соответствующий код бизнес-логики и реализуйте режим MVP.
Сначала создайте в проекте пакет презентатора, затем создайте класс LoginPresenter.java, извлеките связанный с бизнес-логикой код в Activity, чтобы сформировать уровень презентатора входа в систему:
//登录模块的业务逻辑
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;
}
}
На данный момент Activity имеет только соответствующий код, такой как элементы управления макетом интерфейса, то есть слой View становится:
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 остается таким же, как и исходный, без изменений:
//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 разделен на три независимых уровня M, V и P: уровень модели используется только для генерации данных, уровень представления, то есть Activity содержит только соответствующий код, такой как элементы управления макетом интерфейса, и Уровень презентатора отвечает за обработку связанной бизнес-логики. А слой модели и слой представления не взаимодействуют напрямую.
Во-вторых, расширение применимости уровня Presenter
В приведенном выше примере мы используем LoginActivity в качестве уровня представления. Если страница входа в систему становится фрагментом, таким как LoginFragment, необходимо изменить не только код уровня представления, но также необходимо изменить код уровня Presenter, поскольку объект уровня представления, удерживаемый указанным выше LoginPresenter, является LoginActivity, поэтому очевидно, что это неуместно, поскольку уровень Presenter отвечает за обработку бизнес-логики и не заботится о том, является ли уровень представления активностью или фрагментом. не должен влиять на код уровня Presenter.Код уровня Presenter должен быть относительно независимым.
На данный момент нам нужно определить правило (стандарт) для уровня представления, то есть интерфейса. Объект уровня представления, поддерживаемый уровнем Presenter, является интерфейсом, а уровень Presenter не должен обращать внимание на конкретные реализация этого интерфейса. Мы создаем новый пакет представления и создаем интерфейс ILoginView для стандартизации операций пользовательского интерфейса, связанных со страницей входа:
public interface ILoginView {
public void showProgressBar();
public void hideProgressBar();
public void showToast(String toast);
public void jumpSuccessActivity(LoginData loginData);
}
Затем измените ссылку на слой представления, удерживаемую LoginPresenter выше, на интерфейс 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 не нужно заботиться о том, является ли конкретная реализация уровня представления Activity или Fragment, им нужно только реализовать интерфейс ILoginView и переписать абстрактный метод. Это значительно увеличивает применимость уровня презентатора. Даже в соответствии с этой идеей мы можем создать интерфейс для Presenter и позволить LoginPresenter реализовать этот интерфейс.
Окончательная структура пакета должна выглядеть так:
Выше приведен режим MVP, реализованный путем входа в систему в качестве примера.