Java 使用MVP&;Android中的违规行为
我想根据MVP结构转换我的项目&我已经这样做了,但是它违反了MVP设计,因为它在presenter层中保存活动实例 所以,我只是想知道如何将这个项目转换成纯MVP。这里的Validation类是递归的,可以验证许多字段&因为这里它只是用于注册,我将validate方法放在了单独的线程中 这是我的MVP接口Java 使用MVP&;Android中的违规行为,java,android,mvp,Java,Android,Mvp,我想根据MVP结构转换我的项目&我已经这样做了,但是它违反了MVP设计,因为它在presenter层中保存活动实例 所以,我只是想知道如何将这个项目转换成纯MVP。这里的Validation类是递归的,可以验证许多字段&因为这里它只是用于注册,我将validate方法放在了单独的线程中 这是我的MVP接口 import android.app.Activity; public class IMVP_Login { /** * View mandatory methods. Availabl
import android.app.Activity;
public class IMVP_Login {
/**
* View mandatory methods. Available to Presenter
* Presenter -> View
*/
public interface RequiredViewOps {
void showToast(String msg);
}
/**
* Operations offered from Presenter to View
* View -> Presenter
*/
public interface PresenterOps{
void submit(Activity activity);
}
}
这是我的演示者,带有线程,包含活动实例,与MVP的设计模式相反,代码如下
import android.app.Activity;
import java.lang.ref.WeakReference;
import cp.utility.CustomException;
import cp.utility.Validation;
public class PresenterLogin implements Runnable,IMVP_Login.PresenterOps
{
private WeakReference<IMVP_Login.RequiredViewOps> mView;
// this is against the architectural law of MVP
private WeakReference<Activity> activity;
public PresenterLogin(IMVP_Login.RequiredViewOps mView) {
this.mView = new WeakReference<>(mView);
}
@Override
public void run() {
try
{
Validation.validate(activity.get());
}catch (CustomException e)
{
mView.get().showToast(e.getMessage());
}
}
//how should i do this with MVP PATTERN,as it is holding the activity instance
@Override
public void submit(Activity activity) {
this.activity=new WeakReference<>(activity);
Thread validationThread = new Thread(this,"Validation");
validationThread.start();
}
}
这是验证类,取决于EditText的标记
public class Validation {
public static boolean validateFields(final ViewGroup parentView) throws CustomException
{
for (int i = 0; i < parentView.getChildCount(); i++)
{
if (parentView.getChildAt(i) instanceof ViewGroup) {
if ((parentView.getChildAt(i)).getVisibility() == View.VISIBLE)
validateFields((ViewGroup) parentView.getChildAt(i));
}
else if((parentView.getChildAt(i) instanceof TextView) && ((parentView.getChildAt(i)).getVisibility() == View.VISIBLE))
{
TextView editText = (TextView) parentView.getChildAt(i);
if(null!=editText.getTag())
{
String type = editText.getTag().toString().toLowerCase();
String text=GeneralFunction.getTextFromView(editText);
//validation depending on tag
}
}
}
return true;
}
public static boolean validate(Activity activity) {
final ViewGroup viewGroup = (ViewGroup) activity.findViewById(android.R.id.content);
return validateFields(viewGroup);
}
公共类验证{
公共静态布尔validateFields(最终视图组父视图)引发CustomException
{
对于(int i=0;i
}此MVP框架(尤其是android)中添加了单独的presenter类的主要原因是删除OutOfMemory,或者如果活动碰巧失败,则presenter调用不会受到影响,即为什么采用MVP方法而不是MV框架 考虑以下链接中的示例:-
public class MainActivity extends Activity {
public static final String DEFAULT_NAME = "Chuck Norris";
private ArrayAdapter<ServerAPI.Item> adapter;
private Subscription subscription;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView)findViewById(R.id.listView);
listView.setAdapter(adapter = new ArrayAdapter<>(this, R.layout.item));
requestItems(DEFAULT_NAME);
}
@Override
protected void onDestroy() {
super.onDestroy();
unsubscribe();
}
public void requestItems(String name) {
unsubscribe();
subscription = App.getServerAPI()
.getItems(name.split("\\s+")[0], name.split("\\s+")[1])
.delay(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ServerAPI.Response>() {
@Override
public void call(ServerAPI.Response response) {
onItemsNext(response.items);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable error) {
onItemsError(error);
}
});
}
public void onItemsNext(ServerAPI.Item[] items) {
adapter.clear();
adapter.addAll(items);
}
public void onItemsError(Throwable throwable) {
Toast.makeText(this, throwable.getMessage(), Toast.LENGTH_LONG).show();
}
private void unsubscribe() {
if (subscription != null) {
subscription.unsubscribe();
subscription = null;
}
}
}
MainActivity创建MainPresenter并将其保持在onCreate/onDestroy循环的范围之外。MainActivity使用一个静态变量引用MainPresenter,因此每次进程因内存不足事件而重新启动时,MainActivity都应检查presenter是否仍在此处,并在需要时创建它(如文档中所述)
希望这能有所帮助:)让我首先说,有许多不同的MVP方法,每种方法都有其自身的有效性。要记住的重要事项是:
- 视图不应该知道模型,它根本不关心它的数据来自哪里
- 演示者不应该知道Android。您应该能够完全在JVM上运行Presenter类
- 您的Activity/Fragment/ViewGroup应该实现视图界面,这是演示者与他们进行通信的方式
您可以更改在模型中使用的网络库,视图/演示器应该可以正常工作。您可以将视图从水平的ViewPager切换到垂直的RecyclerView,而演示者/模型也不会在意
我们可以模拟演示者并对视图或模型进行单元测试。模拟视图和模型,并对演示者进行单元测试
公共接口登录视图{
映射getLoginFields();
}
活动:
public class LoginActivity extends AppCompatActivity implements LoginView {
private EditText emailView;
private EditText phoneView;
private EditText passwordView;
private Button loginView;
private LoginPresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
presenter = new LoginPresenter();
presenter.bindView(this);
emailView = findViewById(R.id.login_email);
phoneView = findViewById(R.id.login_phone);
passwordView = findViewById(R.id.login_password);
loginView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.login();
}
});
}
@Override
protected void onDestroy() {
presenter.unbindView();
super.onDestroy();
}
@Override
public Map<String, String> getLoginFields() {
Map<String, String> fields = new HashMap<>();
fields.put(emailView.getTag().toString(), emailView.getText().toString());
fields.put(phoneView.getTag().toString(), phoneView.getText().toString());
fields.put(passwordView.getTag().toString(), passwordView.getText().toString());
return fields;
}
}
公共类LoginActivity扩展AppCompatActivity实现LoginView{
私人编辑文本电子邮件视图;
私人编辑文本电话视图;
私有编辑文本密码视图;
私人按钮登录视图;
私人登录presenter演示者;
@凌驾
创建时受保护的void(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\u登录);
presenter=新登录presenter();
presenter.bindView(此);
emailView=findviewbyd(R.id.login\u email);
phoneView=findviewbyd(R.id.login\u电话);
passwordView=findviewbyd(R.id.login\u密码);
loginView.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图v){
login();
}
});
}
@凌驾
受保护的空onDestroy(){
presenter.unbindView();
super.ondestory();
}
@凌驾
公共地图getLoginFields(){
映射字段=新的HashMap();
fields.put(emailView.getTag().toString(),emailView.getText().toString());
fields.put(phoneView.getTag().toString(),phoneView.getText().toString());
fields.put(passwordView.getTag().toString(),passwordView.getText().toString());
返回字段;
}
}
您可能希望使用getLoginFields方法做一些有趣的事情,并在容器中循环。即使您有100个字段,但不需要卸载到另一个线程。如果我必须填写100个字段,我将是一个非常不高兴的用户
演示者:
public class LoginPresenter {
private LoginView view;
private LoginValidator validator;
public void bindView(LoginView view) {
this.view = view;
}
public void unbindView() {
view = null;
}
public void login() {
validator = new LoginValidator();
Map<String, String> fields = view.getLoginFields();
boolean isValid = validator.validate(fields);
}
}
公共类登录resenter{
私人登录视图;
私人登录验证器;
公共视图(LoginView视图){
this.view=视图;
}
公共无效解除绑定视图(){
视图=空;
}
公共无效登录(){
validator=新的LoginValidator();
Map fields=view.getLoginFields();
布尔值isValid=validator.validate(字段);
}
}
验证器:
public class LoginValidator {
public boolean validate(Map<String, String> fields) {
//validation depending on tag
return true;
}
}
公共类登录验证器{
公共布尔验证(映射字段){
//根据标签进行验证
返回true;
}
}
public class LoginActivity extends AppCompatActivity implements LoginView {
private EditText emailView;
private EditText phoneView;
private EditText passwordView;
private Button loginView;
private LoginPresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
presenter = new LoginPresenter();
presenter.bindView(this);
emailView = findViewById(R.id.login_email);
phoneView = findViewById(R.id.login_phone);
passwordView = findViewById(R.id.login_password);
loginView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.login();
}
});
}
@Override
protected void onDestroy() {
presenter.unbindView();
super.onDestroy();
}
@Override
public Map<String, String> getLoginFields() {
Map<String, String> fields = new HashMap<>();
fields.put(emailView.getTag().toString(), emailView.getText().toString());
fields.put(phoneView.getTag().toString(), phoneView.getText().toString());
fields.put(passwordView.getTag().toString(), passwordView.getText().toString());
return fields;
}
}
public class LoginPresenter {
private LoginView view;
private LoginValidator validator;
public void bindView(LoginView view) {
this.view = view;
}
public void unbindView() {
view = null;
}
public void login() {
validator = new LoginValidator();
Map<String, String> fields = view.getLoginFields();
boolean isValid = validator.validate(fields);
}
}
public class LoginValidator {
public boolean validate(Map<String, String> fields) {
//validation depending on tag
return true;
}
}