最近在進行項目的重構,也沒怎么去看flutter,至今還處于入門階段。
就又東一榔頭西一榔頭的寫一些關于mvp的東西了。不過總會繼續(xù)寫完的(只要flutter不涼,其實我甚至考慮要邊看rxjava源碼邊開一篇文章記錄學習(hhh
閑話少說進入正題。
1:關于mvp模式
目前市面上使用的模式主流的有:MVC,MVP,MVVM三種。
mcv模式是模型,視圖,控制器三種不同功能的組合,你中有我我中有你。也不失為一種好的模型,畢竟剩下兩種其實本質上來說也是基于mvc演變而來的。硬要說剩下兩種就比mvc好也不見得。

圖片來源于谷歌的mvp項目示例。單看右側來說,這就是控制器與視圖的交互,但是左邊的model和視圖的交互卻由presenter來代理了,這就降低了耦合度。但是有一個不好的地方就是:會多出不少的代碼需要你進行撰寫。
MVVM呢,目前是前端方面用的比較多,其實安卓也是有使用的,經典的使用就是MVVM+Databinding框架。谷歌現(xiàn)在開放的庫有:

lifecycles 處理UI界面的生命周期,在26版本以后的Support庫中,AppCompatActivity和SupportActivity中都實現(xiàn)了LifecycleOwner,內部已經對UI界面的生命周期做了處理了。
而LiveData是一個抽象類,我們可以存放UI頁面需要的數(shù)據(jù),就是把數(shù)據(jù)包裝在LiveData中了,我們可以觀測LiveData中的數(shù)據(jù)變化,但是LiveData是跟UI的生命周期關聯(lián)的,當UI頁面銷毀了,LiveData的數(shù)據(jù)變化回調是不會執(zhí)行的。
Room 就是一個sqlite數(shù)據(jù)持久化庫,我們也可以使用別的ORM庫。
MVVM的響應式編程還是不錯的,但是本文并不使用這個。說起來RN和flutter其實也是響應式編程。(大膽猜測,這可能是未來的主流
2.關于mvp的使用
先不看其他的示例,我們第一個實現(xiàn)的想法很自然:
public class MainActivity extends AppCompatActivity{
...
private Presenter mPresenter;
public void setPresenter(Presenter presenter){
mPresenter = presenter;
}
...
}
public class Presenter{
private Model mModel;
private MainActivity mView;
public Presenter(Model model,aActivity view){
mModel = model;
mView = view;
mView.setPresenter(this);
}
}
這樣就行了嗎?開始你會覺得很自然,但是琢磨琢磨,是不是發(fā)現(xiàn)這三者的耦合度太高了,相比于mvc實際上并沒有降低多少。那么怎么改進呢?持有實例耦合度太高,那咱們使用接口吧。
接下來怎么做呢?咱們讓Activity實現(xiàn)一個IView的接口,讓Presenter實現(xiàn)IPresenter的接口。
public class MainActivity extends AppCompatActivity implements IView{
....
private IPresenter mPresenter;
public void setPresenter(IPresenter presenter){
mPresenter = presenter;
}
....
}
public class Presenter implements IPresenter{
private IModel mModel;
private IView mView;
public Presenter(IModel model,IView view){
mModel = model;
mView = view;
mView.setPresenter(this);
}
}
再把接口集中管理:
public class MainActivityContract {
public interface IPresenter{
}
public interface IModel {
}
public interface IView{
}
}
其實這里以及和谷歌的示例差不多了。但是我們這里做了model的接口注入,而谷歌官方是沒有的(這個可選可不選,谷歌之所以不用是為了保證全局唯一的數(shù)據(jù)層單例,就不能通過接口強轉,避免數(shù)據(jù)污染。)
那么還有沒有可以優(yōu)化的點呢?還是有的:實現(xiàn)更基礎的V,P接口。
public interface BaseView<T> {
void setPresenter(T presenter);
}
public interface BasePresenter {
void start();
}
這是谷歌官方的例子,先上網站:谷歌示例
這里和谷歌不一樣的是,谷歌選用了fragment作為View,而我們使用了Activity。
為什么選用fragment呢?原因有以下兩點:
- 我們把 Activity 作為一個全局控制類來創(chuàng)建對象,把 Fragment 作為 view,這樣兩者就能各司其職。
- 因為 Fragment 比較靈活,能夠方便的處理界面適配的問題。
而我們選用activity也沒有問題,只要是作為view而不處理其他。
3.再優(yōu)化
上面的代碼是不是就完美了呢?其實并不是的,Presenter是持有view和model兩方的,這里看起來沒啥問題。但是你有沒有想過,model是進行耗時操作呢?
比如說一個網絡請求,正常的retrofit來說,一個call<>方法是作為model,然后再presenter中實現(xiàn),再將數(shù)據(jù)更新給view??雌饋硭坪跏菦]什么問題,對吧?
但是View是必須在主線程中運行的,而網絡操作呢?是耗時的,為了避免ANR,必須要在異步線程中運行,那么這時候View關閉會發(fā)生什么呢?
內存泄漏。這里View被持有,是強引用。

實線為強引用,w是windowmanager。僅做個例子,這里presenter對view持有,會導致無法被回收,GC是并不會回收強引用的,即使這時候內存不夠了。
那么我們的解決方法就呼之欲出了,使用軟引用或者弱引用去取代強引用,為了能讓activity被回收,這里我們使用哪種都行。
我個人覺得弱引用是更好的(網上大部分也是弱引用),畢竟最先被回收的就是弱引用,當強引用斷掉時立馬回收。而軟引用僅僅只是有可能回收。
show my code.
public abstract class BasePresenter<V>{
private Reference<V> mViewReference;
/**
* 建立關聯(lián)(弱引用)
* @param view 界面
*/
public void attachView(V view){
mViewReference=new WeakReference<V>(view);
}
/**
* 獲取view
* @return 持有界面
*/
protected V getView(){
if (isViewAttached()){
return mViewReference.get();
}
return null;
}
/**
* 判斷view是否添加
*/
public boolean isViewAttached(){
return mViewReference != null && mViewReference.get()!=null;
}
/**
* 取消關聯(lián)
*/
public void detachView(){
if(mViewReference != null){
mViewReference.clear();
mViewReference = null;
}
}
/**
* 初始化
*/
public abstract void start();
}
這樣就完了嗎?并不,我們還需要在View的生命周期中進行調用
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachView((V) this);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
}
最終,我們的結構是這樣的:
Presenter:BasePresenter(基礎行為,弱引用和初始化)+IPresenter(接口)
以及我們的xxxxPresenter為具體實現(xiàn)。
View:BaseView(生命周期調用以及創(chuàng)建presenter)+IView(接口) 以及具體實現(xiàn)類
Model:使用全局單例(防止數(shù)據(jù)污染)或者如以上兩個,使用接口注入。
4.另一種實現(xiàn)
其實還有另一種寫法,因為MVP里有大量的依賴以及注入行為,代碼會顯得臃腫復雜,那么這時候就可以使用框架了:Dagger2時安卓的注入框架。
我們可以使用Dagger2實現(xiàn)mvp模式。
但是不好意思我還沒學Dagger2,哈哈哈哈。
dbq,等我學完了我再來補充這一部分。