精华内容
下载资源
问答
  • 危害性MVP:最低可行的可玩产品
  • MVP

    2017-10-24 15:24:09
    MVP从更早的MVC框架演变过来的一种框架,与MVC有一定的相似MVP框架由3部分组成:View负责显示,Presenter负责逻辑处理,Model提供数据。MVP与MVC之间最主要的区别在控制层上,在MVP框架中,View与Model并不直接...

    1、 什么是MVP?
    MVP是一种使用广泛的基础架构模式,使用基于事件驱动的应用框架。MVP从更早的MVC框架演变过来的一种框架,与MVC有一定的相似性。MVP框架由3部分组成:View负责显示,Presenter负责逻辑处理,Model提供数据。MVP与MVC之间最主要的区别在控制层上,在MVP框架中,View与Model并不直接交互,所有的交互放在Presenter中;而在MVC里,View与Model会直接产生一定的交互。MVP的Presenter是框架的控制者,承担了大量的逻辑操作,而MVC的Controller更多时候承担一种转发的作用。因此在App中引入MVP的原因,是为了将此前在Activty中包含的大量逻辑操作放到控制层中,避免Activity的臃肿。MVP的变种有很多,其中使用最广泛的是Passive View模式,即被动视图。在这种模式下,整个框架内部模块之间的逻辑操作均由Presenter控制,View仅仅是整个操作的汇报者和结果接收者,Model根据Presenter的单向调用返回数据(图片来自网络)。并且MVP模式使得View与Model的耦合性更低,降低了Presenter对View的依赖,实现了关注点分离的初衷,方便开发人员的编码和测试工作。

    具体到Android App中,我一般将App根据程序的结构进行纵向划分,对应MVP分别为模型层,UI层和逻辑层。UI层一般包括Activity,Fragment,Adapter等直接和UI相关的类,UI层的Activity在启动之后实例化相应的Presenter,App的控制权后移,由UI转移到Presenter,两者之间的通信通过BroadCast、Handler或者接口完成,只传递事件和结果。举个简单的例子,UI层通知逻辑层(Presenter)用户点击了一个Button,逻辑层(Presenter)自己决定应该用什么行为进行响应,该找哪个模型(Model)去做这件事,最后逻辑层(Presenter)将完成的结果更新到UI层。

    2、 MVP架构存在的问题

    转移逻辑操作之后可能部分较为复杂的Activity内代码量还是不少,于是在分层的基础上再加入模板方法(Template Method)。具体做法是在Activity内部分层。其中最顶层为BaseActivity,不做具体显示,而是提供一些基础样式,Dialog,ActionBar在内的内容,展现给用户的Activity继承BaseActivity,重写BaseActivity预留的方法。如有必要再进行二次继承,App中Activity之间的继承次数最多不超过3次。
    模型层(Model)中的整体代码量是最大的,一般由大量的Package组成,针对这部分需要做的就是在程序设计的过程中,做好模块的划分,进行接口隔离,在内部进行分层。

    这里写图片描述

    3、浅谈MVC、MVP、MVVM架构模式的区别和联系

    一、MVC(Model-View-Controller)

    MVC是比较直观的架构模式,用户操作->View(负责接收用户的输入操作)->Controller(业务逻辑处理)->Model(数据持久化)->View(将结果反馈给View)
    MVC

    二、MVP(Model-View-Presenter)

    MVP是把MVC中的Controller换成了Presenter(呈现),目的就是为了完全切断View跟Model之间的联系,由Presenter充当桥梁,做到View-Model之间通信的完全隔离
    MVP

    三、MVVM(Model-View-ViewModel)

    如果说MVP是对MVC的进一步改进,那么MVVM则是思想的完全变革。它是将“数据模型数据双向绑定”的思想作为核心,因此在View和Model之间没有联系,通过ViewModel进行交互,而且Model和ViewModel之间的交互是双向的,因此视图的数据的变化会同时修改数据源,而数据源数据的变化也会立即反应到View上。

    MVVM

    展开全文
  • MVP基本思想

    2018-12-08 10:33:30
    MVP的逻辑思维都在P层,他降低了页面的耦合度,具备低耦合的特性,MVP的出现使代码更具逻辑 首先我们看到分包的严谨 MVP的结构分析 : P层负责整体逻辑并且将M层和V层联系起来,M层主要负责代码块, Callback将...

    MVP的逻辑性思维都在P层,他降低了页面的耦合度,具备低耦合的特性,MVP的出现使代码更具逻辑性
    在这里插入图片描述
    首先我们看到分包的严谨性
    在这里插入图片描述
    MVP的结构分析 : P层负责整体逻辑并且将M层和V层联系起来,M层主要负责代码块, Callback将结果集返回P层,V层最后展示视图

    注意以下接口:

    public interface MyCallBack<T> {
        void setData(T data);
    }
    
    public interface IModel {
        void startResquestData(String url, String phone, String pass, MyCallBack callBack);
    }
    public interface IPresenter {
        void resquestData(String url,String phone,String pass);
    }
    public interface IView<T> {
        void showResquestData(T data);
    }
    
    工具类
    
    public class MyHttpsUntils {
        public static String get(String murl,String phone,String pass){
            try {
                URL url = new URL(murl+"?username="+phone+"&password="+pass);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                if(connection.getResponseCode()==200){
                    InputStream stream = connection.getInputStream();
                    String s = getStr(stream);
                    return s;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        public static String getWorkData(String murl){
            try {
                URL url = new URL(murl);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                if(connection.getResponseCode()==200){
                    InputStream stream = connection.getInputStream();
                    String s = getStr(stream);
                    return s;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        private static String getStr(InputStream stream) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
            StringBuffer buffer = new StringBuffer();
            String con;
            while((con=reader.readLine())!=null){
                buffer.append(con);
            }
            return buffer.toString();
        };
    }
    

    P层

    public IPresenterImpl(IView iView) {
            this.iView = iView;
            iModel = new IModelImpl();
        }
    
    @Override
    public void resquestData(String url, String phone, String pass) {
        iModel.startResquestData(url, phone, pass, new MyCallBack() {
            @Override
            public void setData(Object data) {
                iView.showResquestData(data);
            }
        });
    }
    

    M层

    public class IModelImpl implements IModel {
        private MyCallBack callBack;
        @SuppressLint("HandlerLeak")
        private Handler mhandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if(msg.what==0){
                    String d = (String) msg.obj;
                    Gson gson = new Gson();
                    MyLogin myLogin = gson.fromJson(d, MyLogin.class);
                    if(myLogin.getData()!=null){
                        MyLogin login = new MyLogin();
                        login.setCode(myLogin.getCode());
                        callBack.setData(login);
                    }else{
                        callBack.setData(myLogin);
                    }
            }
        }
    };
    @Override
    public void startResquestData(final String url, final String phone, final String pass, MyCallBack callBack) {
        this.callBack = callBack;
        new Thread(new Runnable() {
            @Override
            public void run() {
                String data = MyHttpsUntils.get(url,phone,pass);
                mhandler.sendMessage(mhandler.obtainMessage(0,data));
            }
        }).start();
      }
    }
    
    展开全文
  • MVP瘦身方案的可行

    2019-04-16 16:09:18
    MVP瘦身方案的可行 自google发布mvp架构demo以来,越来越多的人开始在项目中尝试使用mvp架构来搭建自己的项目。mvp的发布初衷也是为了将逻辑代码与UI分离,通过接口的方式互通,达到解耦的目的。但用着用着,我们...

    MVP瘦身方案的可行性

    自google发布mvp架构demo以来,越来越多的人开始在项目中尝试使用mvp架构来搭建自己的项目。mvp的发布初衷也是为了将逻辑代码与UI分离,通过接口的方式互通,达到解耦的目的。但用着用着,我们会发现一个蛋疼的事实,因为mvp是通过接口的形式来互相通信,那么就无法避免地要额外写多个接口。

    通常,一个Activity就对应着一个View、一个Presenter和一个Model,这就意味着有多少个Activity,就要多写至少2个接口。如果一个项目中有几十个上百个Activity,那么这增加的代码量可不容小觑,并且开发人员写起来也相当蛋疼。
    贴一张现在项目某个module下presenter包结构图:

    可以看到,光这里的presenter就多达20几个,相应地,model包跟ui包下也差不多都有20几个接口了。

    mvp在项目中的实际操作

    通常,我们是怎样定义mvp各个模块的接口的呢,拿用户端的接单DriverLocationAdvanceActivity来说:

    model层:

    View层:

    presenter层:

    可以看到,View层有三个网络请求的动作,具体的动作实施放在model层,model层通过CallBack接口回调将数据回传给presenter层,presenter层因持有view层的引用,因此调用view层的各个接口将数据传给具体的View完成具体动作的实施。

    改进方案

    view层与presenter层不通过接口,而是通过Message进行通信,这样就可以减少view层跟presenter层的代码量,并且可以重用presenter.
    还是拿上面的例子,修改后的类结构图:

    view层:

    presenter层:

    model层基本不变。

    可以看到,view层中,在IView中增加了一个handleMessage方法,它接收一个Message对象,而presenter层中在每个方法中也都传入了Message对象,那这个Message有是什么呢?

    
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Messenger;
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.text.TextUtils;
    
    /**
     * Defines a message containing a description and arbitrary data object that can be
     * sent to a {@link Handler}.  This object contains two extra int fields and an
     * extra object field that allow you to not do allocations in many cases.
     * <p>
     * <p class="note">While the constructor of Message is public, the best way to get
     * one of these is to call {@link #obtain Message.obtain()} or one of the
     * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
     * them from a pool of recycled objects.</p>
     */
    public final class Message implements Parcelable {
        /**
         * User-defined message code so that the recipient can identify
         * what this message is about. Each {@link Handler} has its own name-space
         * for message codes, so you do not need to worry about yours conflicting
         * with other handlers.
         */
        public int what;
    
        /**
         * arg1 and arg2 are lower-cost alternatives to using
         * {@link #setData(Bundle) setData()} if you only need to store a
         * few integer values.
         */
        public int arg1;
    
        /**
         * arg1 and arg2 are lower-cost alternatives to using
         * {@link #setData(Bundle) setData()} if you only need to store a
         * few integer values.
         */
        public int arg2;
    
    
        public String str;
    
        /**
         * 记录presenter的类名,当一个页面需要持有多个Presenter时使用
         */
        public String presenter;
    
        /**
         * An arbitrary object to send to the recipient.  When using
         * {@link Messenger} to send the message across processes this can only
         * be non-null if it contains a Parcelable of a framework class (not one
         * implemented by the application).   For other data transfer use
         * {@link #setData}.
         * <p>
         * <p>Note that Parcelable objects here are not supported prior to
         * the {@link Build.VERSION_CODES#FROYO} release.
         */
        public Object obj;
    
    
        public Object[] objs;
    
        /**
         * Optional Messenger where replies to this message can be sent.  The
         * semantics of exactly how this is used are up to the sender and
         * receiver.
         */
        public Messenger replyTo;
    
        /**
         * Optional field indicating the uid that sent the message.  This is
         * only valid for messages posted by a {@link Messenger}; otherwise,
         * it will be -1.
         */
        public int sendingUid = -1;
    
        /**
         * If set message is in use.
         * This flag is set when the message is enqueued and remains set while it
         * is delivered and afterwards when it is recycled.  The flag is only cleared
         * when a new message is created or obtained since that is the only time that
         * applications are allowed to modify the contents of the message.
         * <p>
         * It is an error to attempt to enqueue or recycle a message that is already in use.
         */
        /*package*/ static final int FLAG_IN_USE = 1 << 0;
    
        /**
         * If set message is asynchronous
         */
        /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
    
        /**
         * Flags to clear in the copyFrom method
         */
        /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    
        /*package*/ int flags;
    
    
        /*package*/ Bundle data;
    
        /*package*/ IView target;
    
        // sometimes we store linked lists of these things
        /*package*/ Message next;
    
        private static final Object sPoolSync = new Object();
        private static Message sPool;
        private static int sPoolSize = 0;
    
        private static final int MAX_POOL_SIZE = 50;
    
        private static boolean gCheckRecycle = true;
    
        /**
         * Return a new Message instance from the global pool. Allows us to
         * avoid allocating new objects in many cases.
         */
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    
        /**
         * Same as {@link #obtain()}, but copies the values of an existing
         * message (including its target) into the new one.
         *
         * @param orig Original message to copy.
         * @return A Message object from the global pool.
         */
        public static Message obtain(Message orig) {
            Message m = obtain();
            m.what = orig.what;
            m.str = orig.str;
            m.presenter = orig.presenter;
            m.arg1 = orig.arg1;
            m.arg2 = orig.arg2;
            m.obj = orig.obj;
            m.objs = orig.objs;
            m.replyTo = orig.replyTo;
            m.sendingUid = orig.sendingUid;
            if (orig.data != null) {
                m.data = new Bundle(orig.data);
            }
            m.target = orig.target;
    
            return m;
        }
    
        /**
         * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
         *
         * @param v Handler to assign to the returned Message object's <em>target</em> member.
         * @return A Message object from the global pool.
         */
        public static Message obtain(IView v) {
            Message m = obtain();
            m.target = v;
            return m;
        }
    
        public static Message obtain(IView v, Object obj) {
            Message m = obtain();
            m.target = v;
            m.obj = obj;
            return m;
        }
    
        public static Message obtain(IView v, Object[] objs) {
            Message m = obtain();
            m.target = v;
            m.objs = objs;
            return m;
        }
    
    
        public static Message obtain(IView v, Class presenter) {
            Message m = obtain();
            m.target = v;
            m.presenter = presenter.getSimpleName();
            return m;
        }
    
    
        public static Message obtain(IView v, Object obj, Class presenter) {
            Message m = obtain();
            m.target = v;
            m.obj = obj;
            m.presenter = presenter.getSimpleName();
            return m;
        }
    
        public static Message obtain(IView v, Object[] objs, Class presenter) {
            Message m = obtain();
            m.target = v;
            m.objs = objs;
            m.presenter = presenter.getSimpleName();
            return m;
        }
    
    
        /**
         * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
         * <em>what</em> members on the Message.
         *
         * @param v    Value to assign to the <em>target</em> member.
         * @param what Value to assign to the <em>what</em> member.
         * @return A Message object from the global pool.
         */
        public static Message obtain(IView v, int what) {
            Message m = obtain();
            m.target = v;
            m.what = what;
    
            return m;
        }
    
        /**
         * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
         * members.
         *
         * @param v    The <em>target</em> value to set.
         * @param what The <em>what</em> value to set.
         * @param obj  The <em>object</em> method to set.
         * @return A Message object from the global pool.
         */
        public static Message obtain(IView v, int what, Object obj) {
            Message m = obtain();
            m.target = v;
            m.what = what;
            m.obj = obj;
    
            return m;
        }
    
        /**
         * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
         * <em>arg1</em>, and <em>arg2</em> members.
         *
         * @param v    The <em>target</em> value to set.
         * @param what The <em>what</em> value to set.
         * @param arg1 The <em>arg1</em> value to set.
         * @param arg2 The <em>arg2</em> value to set.
         * @return A Message object from the global pool.
         */
        public static Message obtain(IView v, int what, int arg1, int arg2) {
            Message m = obtain();
            m.target = v;
            m.what = what;
            m.arg1 = arg1;
            m.arg2 = arg2;
    
            return m;
        }
    
        /**
         * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
         * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
         *
         * @param v    The <em>target</em> value to set.
         * @param what The <em>what</em> value to set.
         * @param arg1 The <em>arg1</em> value to set.
         * @param arg2 The <em>arg2</em> value to set.
         * @param obj  The <em>obj</em> value to set.
         * @return A Message object from the global pool.
         */
        public static Message obtain(IView v, int what,
                                     int arg1, int arg2, Object obj) {
            Message m = obtain();
            m.target = v;
            m.what = what;
            m.arg1 = arg1;
            m.arg2 = arg2;
            m.obj = obj;
    
            return m;
        }
    
        /**
         * 这个消息是否是这个Presenter发送的
         *
         * @param presenter
         * @return
         */
        public boolean isFromThisPresenter(Class presenter) {
            return this.presenter.equals(presenter.getSimpleName());
        }
    
        /**
         * @hide
         */
        public static void updateCheckRecycle(int targetSdkVersion) {
            if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
                gCheckRecycle = false;
            }
        }
    
        /**
         * Return a Message instance to the global pool.
         * <p>
         * You MUST NOT touch the Message after calling this function because it has
         * effectively been freed.  It is an error to recycle a message that is currently
         * enqueued or that is in the process of being delivered to a Handler.
         * </p>
         */
        public void recycle() {
            if (isInUse()) {
                if (gCheckRecycle) {
                    throw new IllegalStateException("This message cannot be recycled because it "
                            + "is still in use.");
                }
                return;
            }
            recycleUnchecked();
        }
    
        /**
         * Recycles a Message that may be in-use.
         * Used internally by the MessageQueue and Looper when disposing of queued Messages.
         */
        void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            objs = null;
            str = null;
            presenter = null;
            replyTo = null;
            sendingUid = -1;
            target = null;
            data = null;
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
        /**
         * Make this message like o.  Performs a shallow copy of the data field.
         * Does not copy the linked list fields, nor the timestamp or
         * target/callback of the original message.
         */
        public void copyFrom(Message o) {
            this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
            this.what = o.what;
            this.str = o.str;
            this.presenter = o.presenter;
            this.arg1 = o.arg1;
            this.arg2 = o.arg2;
            this.obj = o.obj;
            this.objs = o.objs;
            this.replyTo = o.replyTo;
            this.sendingUid = o.sendingUid;
    
            if (o.data != null) {
                this.data = (Bundle) o.data.clone();
            } else {
                this.data = null;
            }
        }
    
    
        public void setTarget(IView target) {
            this.target = target;
        }
    
        /**
         * Retrieve the a {@link Handler Handler} implementation that
         * will receive this message. The object must implement
         * {@link Handler#handleMessage(android.os.Message)
         * Handler.handleMessage()}. Each Handler has its own name-space for
         * message codes, so you do not need to
         * worry about yours conflicting with other handlers.
         */
        public IView getTarget() {
            return target;
        }
    
    
        /**
         * Obtains a Bundle of arbitrary data associated with this
         * event, lazily creating it if necessary. Set this value by calling
         * {@link #setData(Bundle)}.  Note that when transferring data across
         * processes via {@link Messenger}, you will need to set your ClassLoader
         * on the Bundle via {@link Bundle#setClassLoader(ClassLoader)
         * Bundle.setClassLoader()} so that it can instantiate your objects when
         * you retrieve them.
         *
         * @see #peekData()
         * @see #setData(Bundle)
         */
        public Bundle getData() {
            if (data == null) {
                data = new Bundle();
            }
    
            return data;
        }
    
        /**
         * Like getData(), but does not lazily create the Bundle.  A null
         * is returned if the Bundle does not already exist.  See
         * {@link #getData} for further information on this.
         *
         * @see #getData()
         * @see #setData(Bundle)
         */
        public Bundle peekData() {
            return data;
        }
    
        /**
         * Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
         * as a lower cost way to send a few simple integer values, if you can.
         *
         * @see #getData()
         * @see #peekData()
         */
        public void setData(Bundle data) {
            this.data = data;
        }
    
        /**
         * 废弃,使用 {@link Message#handleMessageToTarget} 代替
         * 分发消息(这里需要自己切换线程),调用 {@link IView#handleMessage(Message)} 处理消息后
         * 将消息放入消息池,供下次 {@link #obtain()}
         */
        @Deprecated
        public void HandleMessageToTarget() {
            if (target == null) throw new IllegalArgumentException("target is null");
            target.handleMessage(this);
            this.recycleUnchecked();
        }
    
        /**
         * 分发消息(这里需要自己切换线程),调用 {@link IView#handleMessage(Message)} 处理消息后
         * 将消息放入消息池,供下次 {@link #obtain()}
         */
        public void handleMessageToTarget() {
            if (target == null) throw new IllegalArgumentException("target is null");
            target.handleMessage(this);
            this.recycleUnchecked();
        }
    
        /**
         * 废弃,使用 {@link Message#handleMessageToTargetUnrecycle} 代替
         * 分发消息(这里需要自己切换线程),调用 {@link IView#handleMessage(Message)} 处理消息
         */
        @Deprecated
        public void HandleMessageToTargetUnrecycle() {
            if (target == null) throw new IllegalArgumentException("target is null");
            target.handleMessage(this);
        }
    
        /**
         * 分发消息(这里需要自己切换线程),调用 {@link IView#handleMessage(Message)} 处理消息
         */
        public void handleMessageToTargetUnrecycle() {
            if (target == null) throw new IllegalArgumentException("target is null");
            target.handleMessage(this);
        }
    
        /**
         * Returns true if the message is asynchronous, meaning that it is not
         * subject to {@link Looper} synchronization barriers.
         *
         * @return True if the message is asynchronous.
         * @see #setAsynchronous(boolean)
         */
        public boolean isAsynchronous() {
            return (flags & FLAG_ASYNCHRONOUS) != 0;
        }
    
        /**
         * Sets whether the message is asynchronous, meaning that it is not
         * subject to {@link Looper} synchronization barriers.
         * <p>
         * Certain operations, such as view invalidation, may introduce synchronization
         * barriers into the {@link Looper}'s message queue to prevent subsequent messages
         * from being delivered until some condition is met.  In the case of view invalidation,
         * messages which are posted after a call to {@link android.view.View#invalidate}
         * are suspended by means of a synchronization barrier until the next frame is
         * ready to be drawn.  The synchronization barrier ensures that the invalidation
         * request is completely handled before resuming.
         * </p><p>
         * Asynchronous messages are exempt from synchronization barriers.  They typically
         * represent interrupts, input events, and other signals that must be handled independently
         * even while other work has been suspended.
         * </p><p>
         * Note that asynchronous messages may be delivered out of order with respect to
         * synchronous messages although they are always delivered in order among themselves.
         * If the relative order of these messages matters then they probably should not be
         * asynchronous in the first place.  Use with caution.
         * </p>
         *
         * @param async True if the message is asynchronous.
         * @see #isAsynchronous()
         */
        public void setAsynchronous(boolean async) {
            if (async) {
                flags |= FLAG_ASYNCHRONOUS;
            } else {
                flags &= ~FLAG_ASYNCHRONOUS;
            }
        }
    
        /*package*/ boolean isInUse() {
            return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
        }
    
        /*package*/ void markInUse() {
            flags |= FLAG_IN_USE;
        }
    
        /**
         * Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
         */
        public Message() {
        }
    
    
        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            b.append("{");
    
            if (target != null) {
    
                b.append(" what=");
                b.append(what);
    
                if (!TextUtils.isEmpty(presenter)) {
                    b.append(" presenter=");
                    b.append(presenter);
                }
    
                if (!TextUtils.isEmpty(str)) {
                    b.append(" str=");
                    b.append(str);
                }
    
    
                if (arg1 != 0) {
                    b.append(" arg1=");
                    b.append(arg1);
                }
    
                if (arg2 != 0) {
                    b.append(" arg2=");
                    b.append(arg2);
                }
    
                if (obj != null) {
                    b.append(" obj=");
                    b.append(obj);
                }
    
                b.append(" target=");
                b.append(target.getClass().getName());
            } else {
                b.append(" barrier=");
                b.append(arg1);
            }
    
            b.append(" }");
            return b.toString();
        }
    
        public static final Creator<Message> CREATOR
                = new Creator<Message>() {
            public Message createFromParcel(Parcel source) {
                Message msg = Message.obtain();
                msg.readFromParcel(source);
                return msg;
            }
    
            public Message[] newArray(int size) {
                return new Message[size];
            }
        };
    
        public int describeContents() {
            return 0;
        }
    
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(what);
            dest.writeInt(arg1);
            dest.writeInt(arg2);
            dest.writeString(str);
            dest.writeString(presenter);
            if (obj != null) {
                try {
                    Parcelable p = (Parcelable) obj;
                    dest.writeInt(1);
                    dest.writeParcelable(p, flags);
                } catch (ClassCastException e) {
                    throw new RuntimeException(
                            "Can't marshal non-Parcelable objects across processes.");
                }
            } else {
                dest.writeInt(0);
            }
    
            if (objs != null) {
                try {
                    Parcelable[] p = (Parcelable[]) objs;
                    dest.writeInt(1);
                    dest.writeParcelableArray(p, flags);
                } catch (ClassCastException e) {
                    throw new RuntimeException(
                            "Can't marshal non-Parcelable objects across processes.");
                }
            } else {
                dest.writeInt(0);
            }
            dest.writeBundle(data);
            Messenger.writeMessengerOrNullToParcel(replyTo, dest);
            dest.writeInt(sendingUid);
        }
    
    
        private void readFromParcel(Parcel source) {
            what = source.readInt();
            arg1 = source.readInt();
            arg2 = source.readInt();
            str = source.readString();
            presenter = source.readString();
            if (source.readInt() != 0) {
                obj = source.readParcelable(getClass().getClassLoader());
            }
            if (source.readInt() != 0) {
                objs = source.readParcelableArray(getClass().getClassLoader());
            }
            data = source.readBundle();
            replyTo = Messenger.readMessengerOrNullFromParcel(source);
            sendingUid = source.readInt();
        }
    }
    

    其实它是一个Message的封装类,它通过obtain的重载方法实例化Message对象的同时与view层绑定。这样在view层中,我们只需要处理handleMessage方法即可,通过msg.what字段来判断处理哪个请求的动作。

    具体实现代码:

    public class DriverLocationAdvancedPresenter extends BasePresenter<OrderContract.Model,OrderContract.View> {
    
        public DriverLocationAdvancedPresenter(OrderContract.Model model, OrderContract.View view) {
            super(model, view);
        }
    
        /**
         * 获取订单详情
         */
        public void loadOrderInfo(final Message msg,String orderUuid){
            mView.showLoading();
            mModel.loadOrderInfo(new OrderContract.CallBack<JsonObject>() {
    
                @Override
                public void succeed(JsonObject data) {
                    mView.hideLoading();
                    msg.what = OrderContract.LOAD_ORDERINFO;
                    msg.obj = data;
                    msg.handleMessageToTarget();
                }
    
                @Override
                public void error() {
                    mView.hideLoading();
                    msg.what = OrderContract.LOAD_ORDERINFO_ERROR;
                    msg.handleMessageToTarget();
                }
            },orderUuid);
        }
    
        /**
         * 查找司机位置
         * @param msg
         * @param orderUuid
         */
        public void searchDriverLocation(final Message msg,String orderUuid){
            mModel.searchDriverLocation(new OrderContract.CallBack<JsonObject>() {
                @Override
                public void succeed(JsonObject data) {
                    msg.what = OrderContract.SEARCH_DRIVERLOCATION;
                    msg.obj = data;
                    msg.handleMessageToTarget();
                }
    
                @Override
                public void error() {
                    msg.what = OrderContract.SEARCH_DRIVERLOCATION_ERROR;
                    msg.handleMessageToTarget();
                }
            },orderUuid);
        }
    
        /**
         * 绑定虚拟号
         * @param orderUuid
         * @param modifyPhone
         */
        public void bindVirtualPhoneNum(final Message msg,String orderUuid,String modifyPhone){
            mModel.bindVirtualPhoneNum(new OrderContract.CallBack<JsonObject>() {
                @Override
                public void succeed(JsonObject data) {
                    msg.what = OrderContract.BIND_VIRTUALPHONENUM;
                    msg.obj = data;
                    msg.handleMessageToTarget();
                }
    
                @Override
                public void error() {
                    msg.what = OrderContract.BIND_VIRTUALPHONENUM_ERROR;
                    msg.handleMessageToTarget();
                }
            },orderUuid,modifyPhone);
        }
    }

    在view层中的处理代码:

     /* presenter事件统一回调
         * @param message {@link Message} 对象, 不能为 {@code null}
         */
        @Override
        public void handleMessage(@NonNull Message message) {
            switch (message.what){
                case OrderContract.LOAD_ORDERINFO://获取订单详情成功
                    JsonObject jsonObject = (JsonObject) message.obj;
                    handleOrderDetailResult(jsonObject);
                    break;
                case OrderContract.LOAD_ORDERINFO_ERROR://获取订单详情失败
                    Toast.makeText(DriverLocationAdvancedActivity.this,"加载订单数据失败",Toast.LENGTH_SHORT).show();
                    break;
                case OrderContract.SEARCH_DRIVERLOCATION://获取司机位置成功
                    JsonObject jsonObject1 = (JsonObject) message.obj;
                    handleDrvierLocationResult(jsonObject1);
                    break;
                case OrderContract.SEARCH_DRIVERLOCATION_ERROR://获取司机位置失败
                    hasDriverLoc = false;
                    break;
                case OrderContract.BIND_VIRTUALPHONENUM://绑定虚拟号成功
                    JsonObject jsonObject2 = (JsonObject) message.obj;
                    handleBindVirtualNum(jsonObject2);
                    break;
                case OrderContract.BIND_VIRTUALPHONENUM_ERROR://绑定虚拟号失败
                    break;
            }
        }

    这样做的好处是,View层跟Presenter层都可以共用,不同的请求动作只需要在presenter中定义对应的方法即可。

    在我看来,mvp瘦身最主要的还是业务粒度大小的划分,粒度划分越小,相应的代码量就越多;粒度划分越大,相应的代码量就减少。只有合理地划分粒度,才能保证整体代码结构的可阅读性。
    这个方案也是借鉴了其他大牛的思想,通过自己的理解加以实现。方案的可行性有待商榷,希望有更好想法或者不同见解的大神能够多多交流或者评论,谢谢。




     

    展开全文
  • MVP模式重用的一些思考

    千次阅读 2017-01-18 18:09:03
    MVP重用的一些思考

    一,概述

    这里写图片描述

    如上图看起来很好理解,其中View中包含了Presenter,Presenter中有View的接口和Model,可以调用View和Model。Model可以说完全独立。MVP与MVC的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的业务逻辑都在Presenter,而在MVC中View会直接从Model中读取数据而不是通过 Controller。

    MVP优点:
    1、对MVC进一步解耦,model与view完全分离
    2、单元测试,因逻辑都在Presenter,presenter的逻辑就可以方便单元测试,还有Model也很好测试
    3、重用性,我们可以把Presenter用于不同的view上,还有Model更加可以独立使用

    二,示例

    下面模式是参考google的todoapp mvp示例

    1,接口封装

    package com.jerrycai.mymvp.login;
    
    import com.jerrycai.mymvp.base.IView;
    import com.jerrycai.mymvp.bean.User;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public interface LoginContract {
    
        interface View extends IView {
    
            void loginSuccess(User user);
    
            void loginFailure(String code, String msg);
    
        }
    
        interface Presenter {
    
            void login(String phone, String pwd);
    
        }
    }

    2,Model

    package com.jerrycai.mymvp.service;
    
    import com.jerrycai.mymvp.callback.Callback;
    
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public class UserService {
        public void login(String phone, String pwd, Callback callback) {
            //连网处理
        }
    }

    3,View

    package com.jerrycai.mymvp.login;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    import com.jerrycai.mymvp.bean.User;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public class LoginActivity extends Activity implements LoginContract.View {
        private LoginContract.Presenter mPresenter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mPresenter = new LoginPresenter(this);
            mPresenter.login("test","test");
        }
    
        @Override
        public void loginSuccess(User user) {
            //成功处理
        }
    
        @Override
        public void loginFailure(String code, String msg) {
            //失败处理
        }
    }
    

    4,Presenter

    package com.jerrycai.mymvp.login;
    
    import com.jerrycai.mymvp.base.BasePresenter;
    import com.jerrycai.mymvp.bean.User;
    import com.jerrycai.mymvp.callback.Callback;
    import com.jerrycai.mymvp.service.UserService;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    
    public class LoginPresenter extends BasePresenter<UserService, LoginContract.View> implements LoginContract.Presenter {
    
        public LoginPresenter(LoginContract.View view) {
            super(new UserService(), view);
        }
    
        @Override
        public void login(String phone, String pwd) {
            mMoudel.login(phone, pwd, new Callback<User>() {
                @Override
                public void onSuccess(User user) {
                    mView.loginSuccess(user);
                }
    
                @Override
                public void Failure(String code, String msg) {
                    mView.loginFailure(code, msg);
                }
            });
        }
    }

    三,一些思考

    现在要加多一个功能,手机登录当然要获取验证码,而找回密码也要获取手机验证码,一般会写成同一个接口不同参数,这样我们的Model,View和Presenter怎么处理呢。

    我看过不少人会Presenter是和界面挂钩,其中包括我以前也这样。若他们分工是A做登录,B做找回密码,他们可能会弄了一个LoginPresenter(有获取验证码的处理和有登录的处理)和一个ForgotPwdPresenter(有获取验证码的处理和有提交找回密码的处理),相应的View实现的功能也会实现一套一样的,Model估计倒不会再加一个。这样重用性明显少了点,model可以重用,presenter却不能重用了。那我们的Presenter不能和界面挂钩应该和model挂钩才对,一个界面应该允许多个不同功能逻辑的presenter,View可以实现多个View接口。下面上代码:

    1,VerificationCodeContract

    package com.jerrycai.mymvp.contract;
    
    import com.jerrycai.mymvp.base.IView;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public interface VerificationCodeContract {
        interface View extends IView {
    
            void onCodeCallback(boolean success, String errorMsg);
    
        }
    
        interface Presenter {
    
            void getCode(String phone, int type);
    
        }
    }

    2,VerificationCodeService

    package com.jerrycai.mymvp.service;
    
    import com.jerrycai.mymvp.callback.Callback;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public class VerificationCodeService {
        public void getCode(String phone, int type, Callback callback) {
            //连网处理
        }
    }

    3,VerificationCodePresenter

    package com.jerrycai.mymvp.presenter;
    
    import com.jerrycai.mymvp.base.BasePresenter;
    import com.jerrycai.mymvp.callback.Callback;
    import com.jerrycai.mymvp.contract.VerificationCodeContract;
    import com.jerrycai.mymvp.service.VerificationCodeService;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public class VerificationCodePresenter extends BasePresenter<VerificationCodeService, VerificationCodeContract.View> implements VerificationCodeContract.Presenter {
        public VerificationCodePresenter(VerificationCodeContract.View view) {
            super(new VerificationCodeService(), view);
        }
    
        @Override
        public void getCode(String phone, int type) {
            mMoudel.getCode(phone, type, new Callback() {
                @Override
                public void onSuccess(Object o) {
                    mView.onCodeCallback(true, null);
                }
    
                @Override
                public void Failure(String code, String msg) {
                    mView.onCodeCallback(false, msg);
                }
            });
        }
    }

    4,LoginActivity

    package com.jerrycai.mymvp.login;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    import com.jerrycai.mymvp.bean.User;
    import com.jerrycai.mymvp.contract.LoginContract;
    import com.jerrycai.mymvp.contract.VerificationCodeContract;
    import com.jerrycai.mymvp.presenter.LoginPresenter;
    import com.jerrycai.mymvp.presenter.VerificationCodePresenter;
    
    /**
     * Created by JerryCai on 2017/1/18.
     */
    
    public class LoginActivity extends Activity implements LoginContract.View,VerificationCodeContract.View {
        private LoginContract.Presenter mPresenter;
        private VerificationCodeContract.Presenter mCodePresenter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //-------------示范用的,直接写在这样而已
            mPresenter = new LoginPresenter(this);
            mPresenter.login("test","test");
    
            mCodePresenter = new VerificationCodePresenter(this);
            mCodePresenter.getCode("test",0);
        }
    
        @Override
        public void loginSuccess(User user) {
            //成功处理
        }
    
        @Override
        public void loginFailure(String code, String msg) {
            //失败处理
        }
    
        @Override
        public void onCodeCallback(boolean success, String errorMsg) {
            //处理验证码
        }
    }

    这样LoginPresenter和VerificationCodePresenter都可以重用,用在不同的东西,还有service肯定也可以的,也便于单元测试

    展开全文
  • 再谈MVP,最小可用产品 之前关于MVP的基本概念在前面一篇博客里面已经提到了,本次课程学习正好又提到了关于MVP,那么就总结一下如何在工作中使用MVP思想。 快速使用MVP的几点原则: - 提前推演逻辑,不要盲目验证 ...
  • 明确目的-拆分需求-竞品分析找到差异点-明确产品核心价值-设计MVP-市场验证-数据分析以及复盘 二,如何设计MVP 1.明确用户需求的必要条件 2.最小化的进行产品设计 3.有效进行数据埋点进行评估 案例:长视频产品广告...
  • 前言 如果你在网上搜 哪种项目架构更好 的时候, 会看到成百上千的博客对各种架构解释优缺点。 但是不幸的是大多数文章都没有提到非常重要的一点: 单元测试 ...让我决定使用 MVP 架构的唯一理由就是它的 简洁 MVP
  • AndroidMVP

    万次阅读 2016-06-22 09:17:58
    Mvp模式简介衍生于MVC 模式,降低了耦合,避免了View(Activity/Fragment)承担了所有的责任, 分担了UI层的职责。在MVP模式里通常包含4个要素: * View:负责绘制UI元素、与用户进行交互(在Android中体现为...
  • MVP模式

    2019-07-18 21:30:32
    Android中的设计模式很多,MVP...MVP模式会解除View和Model的耦合,同时又带来良好的可拓展、可测试,保证系统整洁、灵活。对于一个复杂的应用来说,MVP模式是一种良好的架构模式,它可以非常好地组织应用结...
  • 架构主题服从mvp的思想,在model层做了一些更加细化的划分,model层由各种原子化的usecase组合而成,通过组合的方式使用,以适应复杂的ui需求,在个别耦合比较强的业务之中,甚至可以考虑将这些usecase进行封装,...
  • MVP(Minimum Viable Product,最小可行产品) 在执行MVP的过程中通常会出现两个情况。 创业团队没有足够的设计资源和时间,做出来的前几个版本MVP通常用户体验非常差,这种情况下体验差有可能让用户放弃这款...
  • MVP 模式

    2017-05-13 18:02:59
    MVP 模式 MVP 模式是由 MVC 模式衍生而来。MVP 模式将 Controller 改名为 Presenter,同时改变了...3、 View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动,而 Presenter非常厚,
  • 在上篇内容的基础上,我们已经可以写一些简单的应用了,但是可以预见到的是,随着...这里首先提一下MVP。 什么是MVP? 网络上文章很多。。。。。就不重复了。 Flutter上的MVP实践 这里直接拿代码说话,先说说MVP...
  • 就像微信朋友圈的图片浏览器,可以弹性放大缩小,集成了mvp.
  • MVP详解

    2017-11-02 20:29:06
    M Moudle 主要写耗时操作,例如网络请求。主要做网络处理 V view 视图,例如activity 负责...降低耦合度,模块职责划分明显,利于测试驱动开发,代码复用,隐藏数据,代码灵活 二、为什么使用MVP模式 在A

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,428
精华内容 571
关键字:

性mvp