MVC MVP

開篇

  • 不知道有沒有初學者和我同樣的困惑,MVC和MVP傻傻分不清楚,C和P不就是同樣的嗎,一開始的認知是這樣的:

M:Model層,是那些javabean類,好比自定義的Person類等
V:View層,android裏的layout.xml文件
P:Presenter層,用來解耦網絡請求的,將網絡操做單獨放在這個P類裏java

  • 這就是典型的把MVC和MVP混在一塊兒了,把P和M混在一塊兒

分析

  1. MVC正確的理解

  1. 這裏須要結合代碼看下:
public class MVCActivity extends AppCompatActivity {

    private Button button;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.text_view);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new HttpModel(textView).request();
            }
        });
    }
}



public class HttpModel {
    private TextView textView;

    public HttpModel(TextView textView) {
        this.textView = textView;
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            textView.setText((String) msg.obj);
        }
    };

    public void request() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    Message msg = handler.obtainMessage();
                    msg.obj = "從網絡獲取到的數據";
                    handler.sendMessage(msg);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
複製代碼
  1. 問題是activity作了太多工做,既是view也是controller,而咱們但願在activity中作的工做只是負責view的顯示更新,因此將Controller層的工做抽出來封裝到P層,以前的調用網絡請求由P層去完成,P層調用M層的網絡請求,M層返回結果之後經過P層,P層再更新到V層;這樣能夠作到view層很是簡潔,將一些非view層的初始化邏輯或者調用網絡請求操做交由P層去處理,P層會持有view層的引用,以便須要更新view,而view也會持有P層的引用。
  2. 看下改造後的:
interface MVPView {
    void updateTv(String text);
}


public class MVPActivity extends AppCompatActivity implements MVPView {
    private Button button;
    private TextView textView;
    private Presenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.text_view);
        presenter = new Presenter(this);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.request();
            }
        });
    }

    @Override
    public void updateTv(String text) {
        textView.setText(text);
    }
}


interface Callback {
    void onResult(String text);
}


public class HttpModel {
    private Callback callback;

    public HttpModel(Callback callback) {
        this.callback = callback;
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            callback.onResult((String) msg.obj);
        }
    };

    public void request() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    Message msg = handler.obtainMessage();
                    msg.obj = "從網絡獲取到的數據";
                    handler.sendMessage(msg);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}


public class Presenter {
    private MVPView view;
    private HttpModel model;

    public Presenter(MVPView view) {
        this.view = view;
        model = new HttpModel(new Callback() {
            @Override
            public void onResult(String text) {
                Presenter.this.view.updateTv(text);
            }
        });
    }

    public void request() {
        model.request();
    }
}
複製代碼
  1. 這裏注意有內存泄漏的風險,當activity退出的時候,P層仍持有Activity的引用,P層此時若正在執行耗時操做,則不會被內存回收,間接致使activity不會被回收,形成泄漏。因此要在退出的時候將其引用置空
P層:
public void detachView(){
    view = null;
}
Activity:
@Override
protected void onDestroy(){
    super.onDestroy();
    presenter.detachView();
}
複製代碼

擴展

這裏分析下這個MVP開源庫:github.com/sockeqwe/mo… ,學習一下別人的思想,MVP是一種思想,但實際到項目中使用若是封裝好,用起來就會比較方便android

  1. 看下幾個基本接口、基類
//這個MvpView接口咱們寫一個接口去繼承它,裏面提供通用更新View的方法,
//再讓業務Activity實現該接口,重寫對應方法,在方法中作相應的更新view的操做
public interface MvpView {
}
//P層對應有attachView() detachView() destroy() 三個通用方法
public interface MvpPresenter<V extends MvpView> {

  /**
   * Set or attach the view to this presenter
   */
  @UiThread
  void attachView(@NonNull V view);

  /**
   * Will be called if the view has been destroyed. Typically this method will be invoked from
   * <code>Activity.detachView()</code> or <code>Fragment.onDestroyView()</code>
   *
   * @deprecated This method has been split into 2 methods: {@link #detachView()} and {@link #destroy()}
    */
  @UiThread
  @Deprecated
  void detachView(boolean retainInstance);

  /**
   * Will be called if the view has been detached from the Presenter.
   * Usually this happens on screen orientation changes or view (like fragment) has been put on the backstack.
   */
  @UiThread
  void detachView();

  /**
   * Will be called if the presenter is no longer needed because the View has been destroyed permanently.
   * This is where you do clean up stuff.
   */
  @UiThread
  void destroy();
}

//來看下實現P層接口的通用Presenter類,主要分析下重寫的這三個方法
public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {

  
  private WeakReference<V> viewRef;
  private boolean presenterDestroyed = false;

  @UiThread @Override public void attachView(V view) {
    //傳進來的view是this,實現了V層接口的業務Activity,經過弱引用的方式進行
    //引用,在Acitivy須要被回收的時候,Activity對象不會由於被弱引用引用而致使
    //回收不了,防止內存泄漏
    viewRef = new WeakReference<V>(view);
    presenterDestroyed = false;
  }

  /**
   * Gets the attached view. You should always call {@link #isViewAttached()} to check if the view
   * is attached to avoid NullPointerExceptions.
   * 在Presenter類中,經過此方法來獲取activity對象,調用對應的在activity
   * 中重寫的更新view的方法,一般用法是:
   * if(getView()!=null){ getView().updateView() }
   * 
   * @return <code>null</code>, if view is not attached, otherwise the concrete view instance
   * @deprecated  Use {@link #ifViewAttached(ViewAction)}
   */

  @Deprecated @UiThread public V getView() {
    return viewRef == null ? null : viewRef.get();
  }

  /**
   * {@inheritDoc}
   */
  @Deprecated @UiThread @Override public void detachView(boolean retainInstance) {
  }

  /**
   * {@inheritDoc}此方法也會在activity的onDestroy()中調用,同時還會在屏幕旋轉、
   * Fragment的onDetachView()中調用
   */
  @Override public void detachView() {
    detachView(true);
    if (viewRef != null) {
      viewRef.clear();
      viewRef = null;
    }
  }

  /**
   * {@inheritDoc} 此方法會在徹底退出即activty退出onDestroy()中使用
   */
  @Override public void destroy() {
    detachView(false);
    presenterDestroyed = true;
  }
}
複製代碼
  1. 主要的類和基類Activity:
public class ActivityMvpDelegateImpl<V extends MvpView, P extends MvpPresenter<V>>
    implements ActivityMvpDelegate {

  protected static final String KEY_MOSBY_VIEW_ID = "com.hannesdorfmann.mosby3.activity.mvp.id";

  public static boolean DEBUG = false;
  private static final String DEBUG_TAG = "ActivityMvpDelegateImpl";

  private MvpDelegateCallback<V, P> delegateCallback;
  protected boolean keepPresenterInstance;
  protected Activity activity;
  protected String mosbyViewId = null;

  /**
   * 這裏構造方法會傳進來三個參數,分別是activity,delegateCallback主要是構造Presenter,調用其createPresenter(),
   * getPresenter(),setPresenter()方法,keepPresenterInstance判斷是否要保存Presenter實例
   * @param activity The Activity
   * @param delegateCallback The callback
   * @param keepPresenterInstance true, if the presenter instance should be kept across screen
   * orientation changes. Otherwise false.
   */
  public ActivityMvpDelegateImpl(@NonNull Activity activity,
      @NonNull MvpDelegateCallback<V, P> delegateCallback, boolean keepPresenterInstance) {

    if (activity == null) {
      throw new NullPointerException("Activity is null!");
    }

    if (delegateCallback == null) {
      throw new NullPointerException("MvpDelegateCallback is null!");
    }
    this.delegateCallback = delegateCallback;
    this.activity = activity;
    this.keepPresenterInstance = keepPresenterInstance;
  }

  /**
   * Determines whether or not a Presenter Instance should be kept
   * 在onDestroy()中用於判斷是否保留Presenter實例
   * @param keepPresenterInstance true, if the delegate has enabled keep
   */
  static boolean retainPresenterInstance(boolean keepPresenterInstance, Activity activity) {
    return keepPresenterInstance && (activity.isChangingConfigurations()
        || !activity.isFinishing());
  }

  /**
   * Generates the unique (mosby internal) view id and calls {@link
   * MvpDelegateCallback#createPresenter()}
   * to create a new presenter instance
   *
   * @return The new created presenter instance
   */
  private P createViewIdAndCreatePresenter() {

    //這裏的createPresenter()最終是對應業務activity去重寫的,new出對應的Presenter
    P presenter = delegateCallback.createPresenter();
    if (presenter == null) {
      throw new NullPointerException(
          "Presenter returned from createPresenter() is null. Activity is " + activity);
    }
    //將對應activity和presenter保存
    if (keepPresenterInstance) {
      mosbyViewId = UUID.randomUUID().toString();
      PresenterManager.putPresenter(activity, mosbyViewId, presenter);
    }
    return presenter;
  }

  @Override public void onCreate(Bundle bundle) {

    P presenter = null;

    if (bundle != null && keepPresenterInstance) {

      mosbyViewId = bundle.getString(KEY_MOSBY_VIEW_ID);

      if (DEBUG) {
        Log.d(DEBUG_TAG,
            "MosbyView ID = " + mosbyViewId + " for MvpView: " + delegateCallback.getMvpView());
      }

      if (mosbyViewId != null
          && (presenter = PresenterManager.getPresenter(activity, mosbyViewId)) != null) {
        //
        // Presenter restored from cache
        //
        if (DEBUG) {
          Log.d(DEBUG_TAG,
              "Reused presenter " + presenter + " for view " + delegateCallback.getMvpView());
        }
      } else {
        //
        // No presenter found in cache, most likely caused by process death
        //
        presenter = createViewIdAndCreatePresenter();
        if (DEBUG) {
          Log.d(DEBUG_TAG, "No presenter found although view Id was here: "
              + mosbyViewId
              + ". Most likely this was caused by a process death. New Presenter created"
              + presenter
              + " for view "
              + getMvpView());
        }
      }
    } else {
      // activity第一次啓動,須要建立對應presenter
      // Activity starting first time, so create a new presenter
      //
      presenter = createViewIdAndCreatePresenter();
      if (DEBUG) {
        Log.d(DEBUG_TAG, "New presenter " + presenter + " for view " + getMvpView());
      }
    }

    if (presenter == null) {
      throw new IllegalStateException(
          "Oops, Presenter is null. This seems to be a Mosby internal bug. Please report this issue here: https://github.com/sockeqwe/mosby/issues");
    }

    delegateCallback.setPresenter(presenter);
    //持有對應activity的弱引用
    getPresenter().attachView(getMvpView());

    if (DEBUG) {
      Log.d(DEBUG_TAG, "View" + getMvpView() + " attached to Presenter " + presenter);
    }
  }

  private P getPresenter() {
    P presenter = delegateCallback.getPresenter();
    if (presenter == null) {
      throw new NullPointerException("Presenter returned from getPresenter() is null");
    }
    return presenter;
  }

  private V getMvpView() {
    V view = delegateCallback.getMvpView();
    if (view == null) {
      throw new NullPointerException("View returned from getMvpView() is null");
    }
    return view;
  }

  @Override public void onDestroy() {
    boolean retainPresenterInstance = retainPresenterInstance(keepPresenterInstance, activity);
    //釋放activiy的弱引用
    getPresenter().detachView();
    if (!retainPresenterInstance){
      getPresenter().destroy();
    }
    if (!retainPresenterInstance && mosbyViewId != null) {
      PresenterManager.remove(activity, mosbyViewId);
    }

    if (DEBUG) {
      if (retainPresenterInstance) {
        Log.d(DEBUG_TAG, "View"
            + getMvpView()
            + " destroyed temporarily. View detached from presenter "
            + getPresenter());
      } else {
        Log.d(DEBUG_TAG, "View"
            + getMvpView()
            + " destroyed permanently. View detached permanently from presenter "
            + getPresenter());
      }
    }
  }

  @Override public void onPause() {

  }

  @Override public void onResume() {

  }

  @Override public void onStart() {

  }

  @Override public void onStop() {

  }

  @Override public void onRestart() {

  }

  @Override public void onContentChanged() {

  }

  @Override public void onSaveInstanceState(Bundle outState) {
    if (keepPresenterInstance && outState != null) {
      outState.putString(KEY_MOSBY_VIEW_ID, mosbyViewId);
      if (DEBUG) {
        Log.d(DEBUG_TAG,
            "Saving MosbyViewId into Bundle. ViewId: " + mosbyViewId + " for view " + getMvpView());
      }
    }
  }

  @Override public void onPostCreate(Bundle savedInstanceState) {
  }
}
複製代碼
public abstract class MvpActivity<V extends MvpView, P extends MvpPresenter<V>>
    extends AppCompatActivity implements MvpView,
    com.hannesdorfmann.mosby3.mvp.delegate.MvpDelegateCallback<V,P> {

  protected ActivityMvpDelegate mvpDelegate;
  protected P presenter;
  protected boolean retainInstance;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getMvpDelegate().onCreate(savedInstanceState);
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    getMvpDelegate().onDestroy();
  }

  @Override protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    getMvpDelegate().onSaveInstanceState(outState);
  }

  @Override protected void onPause() {
    super.onPause();
    getMvpDelegate().onPause();
  }

  @Override protected void onResume() {
    super.onResume();
    getMvpDelegate().onResume();
  }

  @Override protected void onStart() {
    super.onStart();
    getMvpDelegate().onStart();
  }

  @Override protected void onStop() {
    super.onStop();
    getMvpDelegate().onStop();
  }

  @Override protected void onRestart() {
    super.onRestart();
    getMvpDelegate().onRestart();
  }

  @Override public void onContentChanged() {
    super.onContentChanged();
    getMvpDelegate().onContentChanged();
  }

  @Override protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    getMvpDelegate().onPostCreate(savedInstanceState);
  }

  /**
   * Instantiate a presenter instance
   * 讓繼承MvpActivity的業務Activity重寫此方法,new出對應的Presenter對象
   * @return The {@link MvpPresenter} for this view
   */
  @NonNull public abstract P createPresenter();

  /**
   * Get the mvp delegate. This is internally used for creating presenter, attaching and detaching
   * view from presenter.
   *
   * <p><b>Please note that only one instance of mvp delegate should be used per Activity
   * instance</b>.
   * </p>
   *
   * <p>
   * Only override this method if you really know what you are doing.
   * </p>
   *
   * @return {@link ActivityMvpDelegateImpl}
   */
  @NonNull protected ActivityMvpDelegate<V, P> getMvpDelegate() {
    if (mvpDelegate == null) {
      mvpDelegate = new ActivityMvpDelegateImpl(this, this, true);
    }

    return mvpDelegate;
  }

  @NonNull @Override public P getPresenter() {
    return presenter;
  }

  @Override public void setPresenter(@NonNull P presenter) {
    this.presenter = presenter;
  }

  @NonNull @Override public V getMvpView() {
    return (V) this;
  }
}
複製代碼
  • 其實以上都是貼源碼,可以大體理解該框架MVP的思想,一些細節問題不用去細扣,看不懂建議你們直接去下源碼來看下,最後以上的理解都是基於此任玉剛大神的博客的理解,若是有看不懂,能夠結合來看
相關文章
相關標籤/搜索