1. 程式人生 > >Android開發中MVP模式

Android開發中MVP模式

傳統的開發模式mvc大家都很熟悉。
在這裡插入圖片描述

View負責頁面展示,Model負責資料。 Controller一個控制協調前兩者的關係,很常見,耦合關係也很明顯。

在常見的android應用開發中Activity類可以是非常複雜的程式碼集合,裡面有各種view,事件,網路請求,資料bean。關聯關係錯綜複雜。對程式碼複用,修改,重構產生了阻礙。

之後又有了MVP,MVVM(雙向繫結)。
在這裡插入圖片描述

在MVC 中M/V之間存在耦合性,通俗的說就是充當V角色的類中有M角色類的匯入和使用。降低耦合性就意味著程式碼之間要創造出更多的距離感,M和V互相沒有直接的關係,卻能各自通過P協同安排完成功能。更近一步,替換V,或者替換M只需要更小的範圍內處理。

在CSDN上看到很多人針對android提出了各自的MVP的寫法,大多是根據業務建立介面IBusiness,然後讓Activity實現這個業務介面。 再建立一個Presenter類,在其中有IBusiness的成員變數。Activity中建立Presenter成員變數,通過Presenter呼叫指定業務方法,最終回撥實現了IBusiness的Activity。 首先你會感覺這樣寫程式碼比以前要複雜了。某個頁面的功能可能非常多,且在開發中易變性。介面雖然做到了一定的隔離,但是易變性帶來的開發繁雜也不容小覷。

最近思考這個開發模式,自己總結了一套東西和大家分享。是不是可以把業務抽象成一項工作任務。 行為和行為的最終承受者。 行為可以是一段程式碼,指向固定的操作,比如請求網路獲取資料,傳送簡訊倒計時。。。。而承受者就是一個View。 比如請求一個列表資料是一項動作。 請求成功後的結果承受者是一個ListView或者RecyclerView. View 應該是具體資料無感,而對資料型別有感,意思就是ListView接受List<?> 而不是List《Dog》,List《Person》. TextView接受String型別作用於自己的setText. 而不需要知道Dog.getName. Person.getName; Button接受View.OnClickListener.而不管裡面是什麼動作,好比一個函式指標。

說了這麼多來個例子。今天我們要實現的是一個定時改變TextView字元的需求。

首先是一個動作類。postValue就是傳遞結果資料。Handler每隔一秒傳遞一次資料。這個類沒有跟任何View關聯。

public class CountDownWork extends BaseViewModel {

    int count  = 0;
    private Handler handler  = new Handler();

    public CountDownWork(@NonNull Application application) {
        super
(application); } @Override public void onCreate() { super.onCreate(); postValue("我是中歐人"); new android.os.Handler().postDelayed(new Runnable() { @Override public void run() { postValue(Tools.concatAll("我是中歐人",java.lang.String.valueOf(count++))); handler.postDelayed(this,1000); } },1000); } }

TestWorkActivity就是我們的試驗場。UseLayout代替了setContentView。這個不用管。 enTrustWork是WorkProviderActivity中的方法,作用是提交一個工作單元,這裡將動作和動作的承受者繫結完畢。

@UseLayout(layoutRes = R.layout.activity_test_provider)
public class TestWorkActivity extends WorkProviderActivity {

    @Override
    public void collectionWork() {
        enTrustWork(new WortUnit(CountDownWork.class,R.id.test_tv));
    }
}

可以看出我們實現一個業務,提煉與View無關的部分程式碼在BaseViewModel子類中。而這個子類是可以直接複用在其他的Activity中。而Activity中則會很清爽,不涉及業務介面,只要管理好自己的View樹展現,並在collectionWork方法中,將自己的View 與動作繫結。

下面簡單貼下原始碼

工作單元。涉及工作內容和工作結果資料的承受者。

public class WortUnit {
    private Class<? extends BaseViewModel> baseViewModel;
    private int viewId;

    public Class<? extends BaseViewModel> getBaseViewModel() {
        return baseViewModel;
    }

    public void setBaseViewModel(Class<? extends BaseViewModel> baseViewModel) {
        this.baseViewModel = baseViewModel;
    }

    public int getViewId() {
        return viewId;
    }

    public void setViewId(int viewId) {
        this.viewId = viewId;
    }

    public WortUnit(Class<? extends BaseViewModel> baseViewModel, int viewId) {
        this.baseViewModel = baseViewModel;
        this.viewId = viewId;
    }
}

WorkProviderActivity類


public   class WorkProviderActivity extends FragmentActivity {

    private List<BaseViewModel> baseViewModels = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        UseLayout layout = this.getClass().getAnnotation(UseLayout.class);
        int layoutRes = 0;
        if (layout != null) {
            layoutRes = layout.layoutRes();
            setContentView(layoutRes);
        }
        collectionWork();
    }

    public  void collectionWork(){
    }

    public void enTrustWork( WortUnit... works){
        if(works!=null && works.length>0){
            for(WortUnit wortUnit:works){
                Class<? extends BaseViewModel> type =  wortUnit.getBaseViewModel();
                if(!baseViewModels.contains(type)){
                    BaseViewModel  baseViewModel = ViewModelProviders.of(this).get(type);
                    baseViewModel.setTtsActivity(this);
                    baseViewModels.add(baseViewModel);

                }
            }


        }

    }






}

BaseViewModel藉助了android 中LiveData,AndroidViewModel ,LifecycleObserver,實現了資料與View的變更通知,元件生命週期方法呼叫。

public class BaseViewModel extends AndroidViewModel implements LifecycleObserver {
    // 建立LiveData
    private MutableLiveData<Object> data = new MutableLiveData<>();
    private WorkProviderActivity ttsActivity;
    private View view;

    private int viewId =View.NO_ID;

    public int getWorkerId() {
        return viewId;
    }

    public void setViewId(int viewId) {
        this.viewId = viewId;
    }

    public BaseViewModel(@NonNull Application application) {
        super(application);
    }

    public WorkProviderActivity getTtsActivity() {
        return ttsActivity;
    }

    public void setTtsActivity(WorkProviderActivity ttsActivity) {
        this.ttsActivity = ttsActivity;
        ttsActivity.getLifecycle().addObserver(this);

    }

    public void setAccount(Object object) {
        data.setValue(object);
    }

    public MutableLiveData<Object> getData() {

        return data;
    }


    @Override
    protected void onCleared() {
        super.onCleared();
    }


    @OnLifecycleEvent(value = Lifecycle.Event.ON_STOP)
    public void onStop() {


    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_START)
    public void onStart() {


    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_CREATE)
    public void onCreate() {

        int viewId = getWorkerId();
        if (viewId == View.NO_ID) {
            AppointWorker appointWorker = this.getClass().getAnnotation(AppointWorker.class);
            if (appointWorker != null) {
                viewId = appointWorker.value();
            }
        }
        if (viewId == View.NO_ID) {
            throw new RuntimeException("請指定工作者的id");
        }
        view = getTtsActivity().findViewById(getWorkerId());
        data.observe(getTtsActivity(), new Observer<Object>() {
            @Override
            public void onChanged(@Nullable Object t) {
                Action2 action2 = IntelligentActions.get(view, t);
                if (action2 != null) {
                    action2.call(view, t);

                }
            }
        });

    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_RESUME)
    public void onResume() {


    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
    public void onPause() {

    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {


    }


    public void postValue(Object value) {
        data.postValue(value);
    }

    public void setValue(Object value) {

        data.setValue(value);
    }

}

關鍵在於這個data.observe操作,在postValue時候,會觸發onChanged。IntelligentActions充當一個智慧的判斷資料和View之間應該擦出什麼樣的火花。比如TextView與String 優先是TextView.setText(String) …

data.observe(getTtsActivity(), new Observer<Object>() {
            @Override
            public void onChanged(@Nullable Object t) {
                Action2 action2 = IntelligentActions.get(view, t);
                if (action2 != null) {
                    action2.call(view, t);

                }
            }
        });
package rx.functions;

/**
 * A two-argument action.
 * @param <T1> the first argument type
 * @param <T2> the second argument type
 */
public interface Action2<T1, T2> extends Action {
    void call(T1 t1, T2 t2);
}

IntelligentActions預先儲存View與資料型別應該做的操作,現在只添加了一些基本的。後續在開發中要新增適配,以及增加使用者定製的優先順序適配規則。

public class IntelligentActions {
    private static HashMap<Class<?>,HashMap<Class<?>,Action2>>actions  = new HashMap();

    public static<T1,T2> Action2<T1,T2> get(T1 view,T2 data){
        HashMap<Class<?>,Action2> action2HashMap =  actions.get(view.getClass());
        Action2 action2 =null;
        if(action2HashMap!=null){
            action2 =  action2HashMap.get(data.getClass());
            if(action2==null) {
                Set<Class<?>> classSet = action2HashMap.keySet();
                for (Class<?> type : classSet) {
                    if (type.isAssignableFrom(view.getClass())) {
                        action2 = action2HashMap.get(type);
                        break;

                    }

                }

            }
        }else{
            action2HashMap =  actions.get(data.getClass());
            if(action2HashMap!=null){
                action2 =  action2HashMap.get(view.getClass());
                if(action2==null) {
                    Set<Class<?>> classSet = action2HashMap.keySet();
                    for (Class<?> type : classSet) {
                        if (type.isAssignableFrom(view.getClass())) {
                            action2 = action2HashMap.get(type);
                            break;

                        }

                    }

                }
            }

        }
        return action2;


    }

    static {
        //全部View 用的click事件
        Action2<View,View.OnClickListener> onClickListenerAction= new Action2<View, View.OnClickListener>() {
            @Override
            public void call(View view, View.OnClickListener onClickListener) {
                view.setOnClickListener(onClickListener);
            }
        };
        HashMap<Class<?>,Action2> clickMap = new HashMap<>();
        clickMap.put(View.OnClickListener.class,onClickListenerAction);
        actions.put(View.OnClickListener.class,clickMap);

        //Textiew和其子類的setText
        HashMap<Class<?>,Action2> textViewMap = new HashMap<>();
        textViewMap.put(String.class, new Action2<TextView,String>() {
            @Override
            public void call(TextView textView, String o2) {
                textView.setText(o2);
            }
        });
        actions.put(TextView.class,textViewMap);

    }
}

在開發模式道路上的一次簡單嘗試。後續要在工作中不斷完善框架。