先說幾個概念
一,Lifecycle
這個玩意是用來管理監聽Actitivy生命週期的一個東西,之前我們可能會寫一個生命週期的回調來做這個事情,比如在對應的生命週期中回調P層來達到監聽的效果,但如果我們寫個自定義控件也要監聽呢,回頭又有一個地方需要監聽呢?當然寫回調可以到達目的,但是很不好管理.所以Google推出這個東西,現在高版本的supper包已經默認支持這個,所以不需要額外引入,需要監聽的類只要實現LifecycleObserver這個接口,並添加到監聽中就可以很好的管理.用法不多說
二,ViewModel+LiveData
這個東西呢有好處,也有一定的弊端,個人認爲.首先呢它可以很好的處理生命週期的問題,數據回調時ui組件生命週期處於不活躍狀態也不會出現空指針的問題,課能這個是最大的好處,然後一點該組件可以及時的收到數據變化的回調,我們不必要管數據是如何變化的,那裏變化的,怎們變化的,只要它變化了我們處理即可.弊端呢,我覺的單純的一個onChange方法很難滿足我們的需求,我們無法做一些其他的回調,比如網絡錯誤,服務器自定義的一些返回碼等這種狀況,必須做一些封裝處理.但我認爲該組件的利大於弊,值得引入項目.
三,Mvp
mvp的概念說爛了,各種各樣,根據自己去搭建適合自己項目的mvp,並非要寫一堆契約類,寫一堆model,仁者見仁,智者見智.之前的項目我會爲M,V,P三層都寫一個擴展接口,然後寫個契約類.標準的Google結構,但是實際項目中,P層作爲一個業務邏輯層,很少會有擴展或者複用的情況.所以我覺得P層完全可以作爲activity的一個邏輯抽離,不必要寫的太複雜.反而model層作爲數據獲取的一層,會有很多的變化,我們必須要做好擴展,View層同理.
四,Dagger2
類似Spring中依賴倒置的一個東西吧,好處是做到了很強的解耦,並做到了很好的統一管理.壞處也有,你寫的代碼,可能換個人會看不懂,即使他會Dagger2!爲神們呢?其實dagger很簡單,無非inject,model這些東西,理解了概念就會寫了,生命週期也好理解,而且也不會用的太多太過複雜.他的難點是神們?是如何去組合model,去寫Component,你總不能每個類,每個需要注入的寫一個文件吧,怎麼寫就看你個人理解,所以我認爲這個是難點!
好,概念介紹完了,來看我們的框架
首先按照常規搭建一個baseActivity(下面是完整的類)
這個地方主要做的就是生命週期的處理,我們寫一個lifecycleObservers 來保存所有的需要回調的對象,並在初始化的時候講P保存到集合中,然後其他的都是一些常規操作,比如顯示toast,狀態欄的相關設置,我們將這些抽取到一個藉口中IBaseView.
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements IBaseView {
@Inject
public P mPresenter;
public Context mContext;
private LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
private ArrayList<LifecycleCallback> lifecycleObservers = new ArrayList<>();
//鍵盤的狀態(彈出/收回)
private boolean isKeyBoardShow;
private Unbinder bind;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initDagger();
mContext = this;
mPresenter.setView(this);
doBeforeSetcontentView();
setContentView(getLayoutRes());
bind = ButterKnife.bind(this);
addLifecycleObserver(mPresenter);
BusManager.getBus().register(this);
initView();
}
/**
* 設置layout前配置
*/
private void doBeforeSetcontentView() {
Window window = getWindow();
window.setBackgroundDrawableResource(R.color.colorEEEEEE);
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
// 無標題
if (getSupportActionBar() != null) {
getSupportActionBar().hide();
}
// 默認着色狀態欄
setStatusBarColor();
SoftKeyBoardListener.setListener((Activity) mContext, new SoftKeyBoardListener.OnSoftKeyBoardChangeListener() {
@Override
public void keyBoardShow(int height) {
isKeyBoardShow = true;
}
@Override
public void keyBoardHide(int height) {
isKeyBoardShow = false;
}
});
}
public boolean isKeyBoardShow() {
return isKeyBoardShow;
}
public void setKeyBoardShow(boolean keyBoardShow) {
isKeyBoardShow = keyBoardShow;
}
/**
* 獲取佈局文件
*
* @return
*/
public abstract int getLayoutRes();
/**
* 子類的後續操作
*/
public abstract void initView();
/**
* 初始化Dagger
*/
public abstract void initDagger();
@Override
protected void onDestroy() {
super.onDestroy();
if (lifecycleRegistry != null) {
removeObservers();
}
if (bind != null) {
bind.unbind();
}
}
/**
* 添加所有的需要監聽的類到監聽器中
*/
public void addLifecycleObserver(LifecycleCallback lifecycleCallback) {
if (lifecycleCallback == null) return;
if (!lifecycleObservers.contains(lifecycleCallback)) {
lifecycleObservers.add(lifecycleCallback);
}
lifecycleRegistry.addObserver(lifecycleCallback);
}
/**
* 移除所有的生命週期監聽器
*/
private void removeObservers() {
for (int i = 0; i < lifecycleObservers.size(); i++) {
LifecycleCallback lifecycleObserver = lifecycleObservers.get(i);
lifecycleRegistry.removeObserver(lifecycleObserver);
}
lifecycleObservers.clear();
}
@Override
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}
@Override
public void setStatusBarTransparent() {
StatusBarUtil.setTransparent(this);
}
@Override
public void setStatusBarColor() {
StatusBarUtil.setColor(this, getResources().getColor(R.color.colorPrimary));
}
@Override
public void setStatusBarColor(int color) {
StatusBarUtil.setColor(this, color);
}
@Override
public void setStatusBarColor(int color, int alpha) {
StatusBarUtil.setColor(this, color, alpha);
}
@Override
public void showShortToast(String content) {
ToastUtil.showLong(this, content);
}
@Override
public void showShortToast(int id) {
ToastUtil.showLong(this, id);
}
@Override
public void showLongToast(String content) {
ToastUtil.showLong(this, content);
}
@Override
public void showLongToast(int id) {
ToastUtil.showLong(this, id);
}
@Override
public void finsActivity() {
ActivityManager.getInstance().finishActivity(this);
}
}
好,然後看一下BasePersenter(下面是完整的類)
我自己寫了一個LifecycleCallback繼承LifecycleObserver ,將生命週期的回調統一寫到了該藉口中,之後那個類像被監聽只需要是相愛藉口即可,也不用去寫生命週期的回調.
P層我們setView方法引用View
public class BasePresenter<V extends IBaseView> implements LifecycleCallback {
public V view;
public BasePresenter() {
}
public void setView(V view) {
this.view = view;
}
public String TAG = getClass().getSimpleName();
@Override
public void onStart() {
Log.e(TAG, "onStart");
}
@Override
public void onCreat() {
Log.e(TAG, "onCreat");
}
@Override
public void onResume() {
Log.e(TAG, "onResume");
}
@Override
public void onPause() {
Log.e(TAG, "onPause");
}
@Override
public void onStop() {
Log.e(TAG, "onStop");
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
}
}
public interface LifecycleCallback extends LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onStart();
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreat();
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume();
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause();
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onStop();
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestroy();
}
之後看一下viewModel+LiveData的處理,
我寫了一個BaseViewModel,在初始化的時候,創建一個CommonLiveData,這個就是要進行操作變化的LiveData,CommonLiveData我做了一些簡單的擴展來處理一些其他事情,比如網絡錯誤等.
BaseErrorHandling 是個神們呢?我理解的可能是我們可能對錯誤碼會做一些全局的處理,比如多端登錄啊,數據處理啊等,但是又有可能部分接口不需要處理,比如有些接口不需要驗證用戶是否登錄神們的,而且每個項目的處理方式是不通的,所以我將其抽取到上層來讓用戶自定義這個錯誤處理器,只需要在ViewModel的構造中竄入一個繼承BaseErrorHandling的自定義Handing即可
public abstract class BaseViewModel<T> extends ViewModel implements IBaseViewModel<T> {
public CommonLiveData<T> liveData;
public BaseViewModel(BaseErrorHandling errorHandling) {
liveData = new CommonLiveData<>();
liveData.setErrorHandling(errorHandling);
}
@Override
public CommonLiveData<T> getData() {
return liveData;
}
}
public class ErrorHandling extends BaseErrorHandling {
public static int TOKENERROR = 401; //參數校驗未通過
public boolean handing(int errorCode, String msg) {
return true;
}
}
@Module
public class ViewModelModel {
@Provides
UserViewModel provideUserViewModel() {
return new UserViewModel(new UserHttpRepertory(),new ErrorHandling());
}
}
然後來看一下CommonLiveData這個東西
我們重寫一下setValue,postValue方法,對空值做一下統一的判斷,當然也可以驗證其他你樂意的東西,
另外我增加了一個setValue(int errorCode, String msg)的重載方法來處理獲取數據出現問題的情況,統一的處理errorHandling已經做過,暴露一個回調commonLiveDataCall來交給業務代碼來處理,這樣就可以處理網絡錯誤啊,數據庫錯誤啊,服務器自定義錯誤啊的種種情況.
public class CommonLiveData<T> extends MutableLiveData<T> {
private CommonLiveDataCall commonLiveDataCall;
private BaseErrorHandling errorHandling;
public void setErrorHandling(BaseErrorHandling errorHandling) {
this.errorHandling = errorHandling;
}
@Override
public void postValue(T value) {
//獲取數據失敗
if (value == null) {
setValue(BaseErrorHandling.NULLERROR, "data can't be null");
} else {
super.postValue(value);
}
}
@Override
public void setValue(T value) {
//獲取數據失敗
if (value == null) {
setValue(BaseErrorHandling.NULLERROR, "data can't be null");
} else {
super.setValue(value);
}
}
public CommonLiveData<T> setCommonLiveDataCall(CommonLiveDataCall commonLiveDataCall) {
this.commonLiveDataCall = commonLiveDataCall;
return this;
}
public void setValue(int errorCode, String msg) {
boolean handing = errorHandling.handing(errorCode, msg);
if (!handing) return;
commonLiveDataCall.dataError(errorCode, msg);
}
public interface CommonLiveDataCall {
void dataError(int errorCode, String msg);
}
}
用法如下
這是一個常規的P層獲取列表的操作
@Override
public void onCreat() {
view.showLoading();
userViewModel.getData().
setCommonLiveDataCall(new CommonLiveData.CommonLiveDataCall() {
@Override
public void dataError(int errorCode, String msg) {
//說明獲取數據失敗了,我們切不管具體錯誤碼,通知view層顯示錯誤的界面
view.showError();
}
}).
observe(view, new Observer<ArrayList<User>>() {
@Override
public void onChanged(@Nullable ArrayList<User> users) {
if (users.size() == 0) {
//顯示空界面
view.showEmpty();
return;
}
view.setData(users);
}
});
changeUser();
}
我們再看viewModel層,該層對livedata做了一個包裝,官方建議數據的改變我們交給Repertory(倉庫)來做,來達到方便擴展的目的,我也這麼建議,而且很有必要
如下是我們的UserViewModel,在構造ViewModel的同時我們將IUserRepertory 倉庫的實現類傳入
public class UserViewModel extends BaseViewModel<ArrayList<User>> {
IUserRepertory userRepertory;
@Inject
public UserViewModel(IUserRepertory userRepertory , BaseErrorHandling errorHandling) {
super(errorHandling);
this.userRepertory = userRepertory;
}
public void loadData() {
userRepertory.getUser(liveData);
}
}
下面模擬一個網絡數據的獲取
public class UserHttpRepertory extends HttpRepertory implements IUserRepertory {
@Inject
public UserHttpRepertory() {
}
@Override
public void getUser(final CommonLiveData<ArrayList<User>> liveData) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Random random = new Random();
User user = new User();
user.name = String.valueOf(random.nextInt(10000000));
user.phone = String.valueOf(random.nextInt(10000000));
user.userId = String.valueOf(random.nextInt(10000000));
users.add(user);
}
// liveData.setValue(BaseErrorHandling.HTTPERROR,"網絡錯誤");
liveData.setValue(users);
}
}, 1000);
}
}
之後如果你要換數據獲取的方式,只需要更改dagger2配置文件,如下
我們就將這個數據的獲取方式換成了數據庫獲取,數據庫的獲取方式自己具體實現,P層,view層,甚至viewmodel不需要做任何的變更
@Module
public class ViewModelModel {
@Provides
UserViewModel provideUserViewModel() {
return new UserViewModel(new UserDaoRepertory(),new ErrorHandling());
}
}
好了,大體的一個實錄就是這樣的,我們還可以做更多的封裝,下面是幾點建議
1,每個數據結構對應一個viewModel,每個viewModel只對應一個數據結構.
2,每個Repertory可以有多個數據結構的改變,具體自己考慮,比方user相關的數據獲取可以放到一個倉庫,goods相關的可以放到一個倉庫等
3,關於dagger2,我建議可以將所有的viewmode寫到一個model配置文件中,改的時候也好找,其他的不涉及到第三方的構造或者接口的直接構造加個injiect就可以了,沒必要單獨出來寫個model,所以總之model我建議分類放(viewmodel,adapter等等)
4,Component 注入器更具自己考慮寫,我建議更具ui界面分開寫,方便管理,畢竟需要注入的東西課能都不一樣,當然寫到一個裏面也可以,便於封裝.
差不多就這些內容,有什麼不足的請多多指教.