在处理 MVP 模式时如何使用 Retrofit call.enqueue() 方法(正确的方法)
How to use Retrofit call.enqueue() method (the right way) when dealing with MVP pattern
我是 android 的新手,这是我第一次尝试在我的代码中使用 MVP 模式......据我所知,到目前为止,我的观点应该与演示者交谈,而演示者模特 > 然后主持人将再次讲话以查看..我希望我是对的!如下图所示,在我的简单代码示例中,我试图 return 从模型到演示者的结果值,然后我将在演示者中使用这个结果值来决定我应该在视图中调用哪个方法。我有 2 个问题,希望对您有所帮助。
1) enqueue 方法异步工作,结果值将始终 return 为空或失败或其他任何原因......因为它单独工作......当我尝试使用 execute 方法时,我面临 NetworkOnMainThreadException 错误...那么我怎样才能做出正确的方法呢?
2) 这是使用 MVP 模式的正确方法吗?
这是注册合同class
public class SignupContract {
public interface IView{
void signupSuccess();
void signupFailed(String message);
}
public interface IPresenter{
void signup(UserProfile userProfile);
}
public interface IModel{
String signup(UserProfile userProfile);
}
}
这是查看代码..
public class SignupActivity extends AppCompatActivity implements SignupContract.IView {
//some code
@Override
protected void onCreate(Bundle savedInstanceState) {
//some code
createAccountBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//some code
presenter.signup(userProfile);
}
});
}
@Override
public void signupSuccess() {
/*AppUtils.dismissLoadingDialog(SignupActivity.this,"","");*/
Intent intent = new Intent(SignupActivity.this, SigninActivity.class);
startActivity(intent);
finish();
}
@Override
public void signupFailed(String message) {
/*AppUtils.dismissLoadingDialog(SignupActivity.this,"","");*/
AppUtils.showErrorMessage(SignupActivity.this, message);
}
这是主持人
public class SignupPresenter implements SignupContract.IPresenter {
SignupContract.IView view;
SignupContract.IModel model;
public SignupPresenter(SignupContract.IView view){
model = new SignupModel();
this.view = view;
}
@Override
public void signup(UserProfile userProfile) {
userProfile = UserProfileCleaner.clean(userProfile, "signup");
UserProfileDTO dto = new UserProfileDTO();
String validationMessage = dto.validateUserProfile(userProfile, "signup");
if(validationMessage != null && !validationMessage.equals("")){
view.signupFailed(validationMessage);
}else{
String signupResult = model.signup(userProfile);
if(signupResult.equals("success")){
view.signupSuccess();
}else {
view.signupFailed(signupResult);
}
}
}
}
这是模特class
public class SignupModel implements SignupContract.IModel {
private String TAG = "SignupModel";
private String result = "";
@Override
public String signup(UserProfile userProfile) {
final Context context = DKApp.getContext();
ServiceWrapper serviceWrapper = new ServiceWrapper(null);
Call<SignupResponse> userSignUpCall = serviceWrapper.userSignUpCall(userProfile.getUser().getUsername(),
userProfile.getUser().getPassword(),userProfile.getPhoneNumber(), userProfile.getEmailAddress(),
userProfile.getFullName());
userSignUpCall.enqueue(new Callback<SignupResponse>() {
@Override
public void onResponse(Call<SignupResponse> call, Response<SignupResponse> response) {
if( response.body() != null && response.isSuccessful() ){
Log.e(TAG,response.body().toString());
if(response.body().getStatus() == 1){
//some code
result = "success";
}else{
result = response.body().getMessage();
}
}else{
result = context.getResources().getString(R.string.request_failed);
}
}
@Override
public void onFailure(Call<SignupResponse> call, Throwable t) {
Log.e(TAG, "Failure : " + t.toString());
result = context.getResources().getString(R.string.request_failed);
}
});
return result;
}
}
您正在模型中进行异步调用,这可能需要 100 毫秒或 2-4 秒,因此从中获取 signupResult 就像 String signupResult = model.signup(userProfile);
这是错误的。
您需要进行的更改:
1) 给IPresenter添加onComplete方法并改变IModel
public interface IPresenter{
void signup(UserProfile userProfile);
//add
void onComplete(String signUpresult);
}
public interface IModel{
//changed
void signup(UserProfile userProfile);
}
2) 在您的 SignupPresenter 中将演示者的实例传递给模型
public class SignupPresenter implements SignupContract.IPresenter {
..
public SignupPresenter(SignupContract.IView view){
model = new SignupModel(this);
this.view = view;
}
...
@Overrides
public void onComplete(String signupResult){
if(signupResult.equals("success")){
view.signupSuccess();
}else {
view.signupFailed(signupResult);
}
}
...
}
3) 在您的 SignupModel 中,一旦获得结果,请从演示者那里调用 onComplete(//result)
public class SignupModel implements SignupContract.IModel {
SignupPresenter presenter;
public SignupModel(SignupPresenter presenter){
this.presenter = presenter
}
@Override
public void signup(UserProfile userProfile) {
...
userSignUpCall.enqueue(new Callback<SignupResponse>() {
@Override
public void onResponse(Call<SignupResponse> call, Response<SignupResponse> response) {
if(response.body() != null && response.isSuccessful() ){
if(response.body().getStatus() == 1){
//some code
presenter.onComplete("success");
}else{
presenter.onComplete(response.body().getMessage());
}
}else{
presenter.onComplete(context.getResources().getString(R.string.request_failed));
}
}
@Override
public void onFailure(Call<SignupResponse> call, Throwable t) {
Log.e(TAG, "Failure : " + t.toString());
presenter.onComplete(context.getResources().getString(R.string.request_failed));
}
});
}
}
需要: 在调用注册时显示进度对话框,并在 SignupPresenter 的 onComplete 上关闭它。
你的理解力还不错,也知道模特也要和presenter对话。要了解有关 MVP 设计模式的更多信息,请阅读 this
在 MVP 模式中,您应该使视图虚拟化。 Presenter 总是告诉视图该做什么。
这是一个例子:
在视图中 =>
presenter.login(userName, password)
在演示者中 =>
fun login(userName: String, password: String) {
//login logic
if(success) view.showLoginSuccess()
else view.showLoginError()
}
这是对 MVP 模式的非常简短的解释。关于您的问题,您不能在主线程上提出请求。幸运的是 Retrofit 有一个线程管理系统。但是你不应该在这样的模型中使用它,你可以在演示者中使用它,或者搜索干净的架构文章以获得更正确的方法。如果你在演示者中使用它,你应该做类似
的事情
userSignUpCall.enqueue(new Callback<SignupResponse>() {
@Override
public void onResponse(Call<SignupResponse> call, Response<SignupResponse> response) {
if( response.body() != null && response.isSuccessful() ){
Log.e(TAG,response.body().toString());
if(response.body().getStatus() == 1){
//some code
view.showMessage("success");
}else{
view.showMessage(response.body().getMessage());
}
}else{
view.showError(context.getResources().getString(R.string.request_failed));
}
}
@Override
public void onFailure(Call<SignupResponse> call, Throwable t) {
Log.e(TAG, "Failure : " + t.toString());
view.showError(context.getResources().getString(R.string.request_failed));
}
});
我是 android 的新手,这是我第一次尝试在我的代码中使用 MVP 模式......据我所知,到目前为止,我的观点应该与演示者交谈,而演示者模特 > 然后主持人将再次讲话以查看..我希望我是对的!如下图所示,在我的简单代码示例中,我试图 return 从模型到演示者的结果值,然后我将在演示者中使用这个结果值来决定我应该在视图中调用哪个方法。我有 2 个问题,希望对您有所帮助。 1) enqueue 方法异步工作,结果值将始终 return 为空或失败或其他任何原因......因为它单独工作......当我尝试使用 execute 方法时,我面临 NetworkOnMainThreadException 错误...那么我怎样才能做出正确的方法呢? 2) 这是使用 MVP 模式的正确方法吗?
这是注册合同class
public class SignupContract {
public interface IView{
void signupSuccess();
void signupFailed(String message);
}
public interface IPresenter{
void signup(UserProfile userProfile);
}
public interface IModel{
String signup(UserProfile userProfile);
}
}
这是查看代码..
public class SignupActivity extends AppCompatActivity implements SignupContract.IView {
//some code
@Override
protected void onCreate(Bundle savedInstanceState) {
//some code
createAccountBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//some code
presenter.signup(userProfile);
}
});
}
@Override
public void signupSuccess() {
/*AppUtils.dismissLoadingDialog(SignupActivity.this,"","");*/
Intent intent = new Intent(SignupActivity.this, SigninActivity.class);
startActivity(intent);
finish();
}
@Override
public void signupFailed(String message) {
/*AppUtils.dismissLoadingDialog(SignupActivity.this,"","");*/
AppUtils.showErrorMessage(SignupActivity.this, message);
}
这是主持人
public class SignupPresenter implements SignupContract.IPresenter {
SignupContract.IView view;
SignupContract.IModel model;
public SignupPresenter(SignupContract.IView view){
model = new SignupModel();
this.view = view;
}
@Override
public void signup(UserProfile userProfile) {
userProfile = UserProfileCleaner.clean(userProfile, "signup");
UserProfileDTO dto = new UserProfileDTO();
String validationMessage = dto.validateUserProfile(userProfile, "signup");
if(validationMessage != null && !validationMessage.equals("")){
view.signupFailed(validationMessage);
}else{
String signupResult = model.signup(userProfile);
if(signupResult.equals("success")){
view.signupSuccess();
}else {
view.signupFailed(signupResult);
}
}
}
}
这是模特class
public class SignupModel implements SignupContract.IModel {
private String TAG = "SignupModel";
private String result = "";
@Override
public String signup(UserProfile userProfile) {
final Context context = DKApp.getContext();
ServiceWrapper serviceWrapper = new ServiceWrapper(null);
Call<SignupResponse> userSignUpCall = serviceWrapper.userSignUpCall(userProfile.getUser().getUsername(),
userProfile.getUser().getPassword(),userProfile.getPhoneNumber(), userProfile.getEmailAddress(),
userProfile.getFullName());
userSignUpCall.enqueue(new Callback<SignupResponse>() {
@Override
public void onResponse(Call<SignupResponse> call, Response<SignupResponse> response) {
if( response.body() != null && response.isSuccessful() ){
Log.e(TAG,response.body().toString());
if(response.body().getStatus() == 1){
//some code
result = "success";
}else{
result = response.body().getMessage();
}
}else{
result = context.getResources().getString(R.string.request_failed);
}
}
@Override
public void onFailure(Call<SignupResponse> call, Throwable t) {
Log.e(TAG, "Failure : " + t.toString());
result = context.getResources().getString(R.string.request_failed);
}
});
return result;
}
}
您正在模型中进行异步调用,这可能需要 100 毫秒或 2-4 秒,因此从中获取 signupResult 就像 String signupResult = model.signup(userProfile);
这是错误的。
您需要进行的更改:
1) 给IPresenter添加onComplete方法并改变IModel
public interface IPresenter{
void signup(UserProfile userProfile);
//add
void onComplete(String signUpresult);
}
public interface IModel{
//changed
void signup(UserProfile userProfile);
}
2) 在您的 SignupPresenter 中将演示者的实例传递给模型
public class SignupPresenter implements SignupContract.IPresenter {
..
public SignupPresenter(SignupContract.IView view){
model = new SignupModel(this);
this.view = view;
}
...
@Overrides
public void onComplete(String signupResult){
if(signupResult.equals("success")){
view.signupSuccess();
}else {
view.signupFailed(signupResult);
}
}
...
}
3) 在您的 SignupModel 中,一旦获得结果,请从演示者那里调用 onComplete(//result)
public class SignupModel implements SignupContract.IModel {
SignupPresenter presenter;
public SignupModel(SignupPresenter presenter){
this.presenter = presenter
}
@Override
public void signup(UserProfile userProfile) {
...
userSignUpCall.enqueue(new Callback<SignupResponse>() {
@Override
public void onResponse(Call<SignupResponse> call, Response<SignupResponse> response) {
if(response.body() != null && response.isSuccessful() ){
if(response.body().getStatus() == 1){
//some code
presenter.onComplete("success");
}else{
presenter.onComplete(response.body().getMessage());
}
}else{
presenter.onComplete(context.getResources().getString(R.string.request_failed));
}
}
@Override
public void onFailure(Call<SignupResponse> call, Throwable t) {
Log.e(TAG, "Failure : " + t.toString());
presenter.onComplete(context.getResources().getString(R.string.request_failed));
}
});
}
}
需要: 在调用注册时显示进度对话框,并在 SignupPresenter 的 onComplete 上关闭它。
你的理解力还不错,也知道模特也要和presenter对话。要了解有关 MVP 设计模式的更多信息,请阅读 this
在 MVP 模式中,您应该使视图虚拟化。 Presenter 总是告诉视图该做什么。 这是一个例子:
在视图中 =>
presenter.login(userName, password)
在演示者中 =>
fun login(userName: String, password: String) {
//login logic
if(success) view.showLoginSuccess()
else view.showLoginError()
}
这是对 MVP 模式的非常简短的解释。关于您的问题,您不能在主线程上提出请求。幸运的是 Retrofit 有一个线程管理系统。但是你不应该在这样的模型中使用它,你可以在演示者中使用它,或者搜索干净的架构文章以获得更正确的方法。如果你在演示者中使用它,你应该做类似
的事情userSignUpCall.enqueue(new Callback<SignupResponse>() {
@Override
public void onResponse(Call<SignupResponse> call, Response<SignupResponse> response) {
if( response.body() != null && response.isSuccessful() ){
Log.e(TAG,response.body().toString());
if(response.body().getStatus() == 1){
//some code
view.showMessage("success");
}else{
view.showMessage(response.body().getMessage());
}
}else{
view.showError(context.getResources().getString(R.string.request_failed));
}
}
@Override
public void onFailure(Call<SignupResponse> call, Throwable t) {
Log.e(TAG, "Failure : " + t.toString());
view.showError(context.getResources().getString(R.string.request_failed));
}
});