Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/rest/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用MVP&;Android中的违规行为_Java_Android_Mvp - Fatal编程技术网

Java 使用MVP&;Android中的违规行为

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

我想根据MVP结构转换我的项目&我已经这样做了,但是它违反了MVP设计,因为它在presenter层中保存活动实例

所以,我只是想知道如何将这个项目转换成纯MVP。这里的Validation类是递归的,可以验证许多字段&因为这里它只是用于注册,我将validate方法放在了单独的线程中

这是我的MVP接口

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,而演示者/模型也不会在意

  • 测试
    我们可以模拟演示者并对视图或模型进行单元测试。模拟视图和模型,并对演示者进行单元测试

  • 只要您使用的MVP实现允许上述情况,那么在我看来它是有效的

    你的具体问题。我会这样设置:

    视图:

    公共接口登录视图{
    映射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;
        }
    }