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);
}
}
在開發模式道路上的一次簡單嘗試。後續要在工作中不斷完善框架。