Base Library部分把App中Application,UI(activity,fragment)公用方法重新封装成模板方法,并开放对子类的扩展。同时融入mvp设计思想,封装成基于mvp的基层架构体系。
目录
1,IApplication(接口):
2,BaseApplication(抽象基类)
2.1 规定Application中行为的执行规则(模板方法模式)
2.2 实现IApplication中的部分行为(通用部分)
2.3 Application关键方法
2.4 定义与公共行为相关的抽象方法(具体行为),供子类扩展
2.5 缓存管理Activity
3,mvp(基层mvp架构)
3.1. mvp策略微调
3.1.1.PV之间通讯采用EventBus消息机制替换之前的接口回调机制。
3.1.2.增加对P的缓存管理
3.2. 契约类 Contract
3.3. V层
1. Activity
3.3.2. Fragment
3.4. P层
3.4.1. Presenter
3.4.2. Event
3.5. M层
4,GlobalField
总结
1,把框架内应用(Application)的通用行为抽象定义(框架内app都会用到的初始化或者公参配置的行为方法)。
2,继承ActivityLifecycleCallbacks接口,整合Activity生命周期回调方法。
之后框架内所有app的自定义Application应该都实现此接口,具体行为放在各个app的Application中实现。
public interface IApplication extends Application.ActivityLifecycleCallbacks {
/**
* 初始化组件(生命周期)(组件化)
*/
void initModules();
/**
* 初始化Router(组件化)
*/
void initRouter();
/**
* init net config
*/
void initNetworkConfig();
/**
* create SharedPreference by name
*
* @param sharedName SharedPreference name
*/
void initSharedPreference(String sharedName);
/**
* other config
*/
void initOtherConfig();
}
也就是说,每个app的Application做了两部分事情,一部分是实现了公共行为(IApplication)(共性),另外一部分实现了私有行为(异性)。
public abstract class BaseApplication extends MultiDexApplication implements IApplication
BaseApplication需要做四件事情:
@Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
registerActivityLifecycleCallbacks(this);
/*
* 组件化框架使用,非组件化无用
*/
initModules();
/*
* 组件化框架使用,非组件化无用
*/
initRouter();
initNetworkConfig();
initSharedPreference(defineSharedPreferenceName());
initOtherConfig();
/*
* 组件化框架调用组件生命周期,非组件化无用
*/
ModuleRouter.getInstance().onModulesCreate(defineModulesNeedManagedByModuleName());
}
@Override
public void initModules() {
ModuleRouter.getInstance().loadModules(this);
}
@Override
public void initSharedPreference(String sharedName) {
SharedUtils.initInstance(getApplicationContext(), sharedName);
}
@Override
public void onTerminate() {
super.onTerminate();
/*
* 组件化框架调用组件生命周期,非组件化无用
*/
ModuleRouter.getInstance().
onModulesTerminate(defineModulesNeedManagedByModuleName());
unregisterActivityLifecycleCallbacks(this);
}
@Override
public void onLowMemory() {
super.onLowMemory();
/*
* 组件化框架调用组件生命周期,非组件化无用
*/
ModuleRouter.getInstance().
onModulesLowMemory(defineModulesNeedManagedByModuleName());
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
/*
* 组件化框架调用组件生命周期,非组件化无用
*/
ModuleRouter.getInstance().
onModulesTrimMemory(defineModulesNeedManagedByModuleName(), level);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
/*
* 组件化框架调用组件生命周期,非组件化无用
*/
ModuleRouter.getInstance().
onModulesConfigurationChanged(defineModulesNeedManagedByModuleName(), newConfig);
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
本类库中,利用Activity生命周期回调方法,由BaseApplication对Activity对应P层的注册,解注行为进行统一管理。支持Activity,AppCompatActivity,FragmentActivity。
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity instanceof BaseActivity) {
((BaseActivity) activity).registerPresenter();
} else if (activity instanceof BaseAppCompatActivity) {
((BaseAppCompatActivity) activity).registerPresenter();
} else if (activity instanceof BaseFragmentActivity) {
((BaseFragmentActivity) activity).registerPresenter();
}
}
@Override
public void onActivityDestroyed(Activity activity) {
if (activity instanceof BaseActivity) {
((BaseActivity) activity).unregisterPresenter();
} else if (activity instanceof BaseAppCompatActivity) {
((BaseAppCompatActivity) activity).unregisterPresenter();
} else if (activity instanceof BaseFragmentActivity) {
((BaseFragmentActivity) activity).unregisterPresenter();
}
}
protected abstract String defineSharedPreferenceName();
/**
* define modules which need managed in Application lifecycle(组件化使用)
*/
protected abstract String[] defineModulesNeedManagedByModuleName();
// other methods ...
/**
* app有效activity缓存
*/
private List> activityList = new ArrayList();
/**
* app当前activity缓存
*/
private List> currentActivityList = new ArrayList();
…… ……
/**
* get current activity
* @return Activity
*/
public Activity getCurrentActivity() {
return currentActivityList.size() == 0 ? null : currentActivityList.get(0).get();
}
/**
* kill all activity
*/
public void killAllActivity() {
for (WeakReference activity : activityList) {
if (null != activity && null != activity.get()) {
activity.get().finish();
}
}
}
/**
* kill all activities Except!!!
* @param activityClass Except activity class
*/
@SuppressWarnings("unchecked")
public void killAllActivityExcept(Class activityClass) {
for (WeakReference weakReference : activityList) {
if (null != weakReference && null != weakReference.get() && !weakReference.get().getClass().equals(activityClass)) {
weakReference.get().finish();
}
}
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
activityList.add(new WeakReference<>(activity));
// ……
}
@Override
public void onActivityStarted(Activity activity) {
if (currentActivityList.size() == 0) {
ModuleRouter.getInstance().
enterForeground(defineModulesNeedManagedByModuleName());
}
currentActivityList.add(new WeakReference<>(activity));
}
@SuppressWarnings("unchecked")
@Override
public void onActivityStopped(Activity activity) {
for (Iterator iterator = currentActivityList.iterator(); iterator.hasNext(); ) {
WeakReference weakReference = (WeakReference) iterator.next();
if (null != weakReference && null != weakReference.get() && weakReference.get() == activity) {
iterator.remove();
}
}
if (currentActivityList.size() == 0) {
ModuleRouter.getInstance().
enterBackground(defineModulesNeedManagedByModuleName());
}
}
@SuppressWarnings("unchecked")
@Override
public void onActivityDestroyed(Activity activity) {
// ……
for (Iterator iterator = activityList.iterator(); iterator.hasNext(); ) {
WeakReference weakReference = (WeakReference) iterator.next();
if (null != weakReference && null != weakReference.get()
&& weakReference.get() == activity) {
iterator.remove();
}
}
}
mvp最近很火,也很烦,各个公司,各位大神,对mvp的理解和应用各异,论坛上各种对mvp的详解,demo,设计分析以及基于mvp的各种开源项目 ……
作者浅见:mvp就是一种架构设计思想,目的在于把UI展现层,业务逻辑处理层,数据管理层在代码中解耦,达到某种程度的代码层次清晰明了(我的理解:某种程度!!!)。至于实际应用中以何种方式设计实现,达到何种程度? …… 因人因项目因公司而已吧,设计成本,团队理解力,学习成本,代码可读性等等……哪种才是最好的?达到目的,适合自己就够了,别出严重bug就好。另外,作者后续会对mvp的个人理解整理成文,敬请关注!
作者在接触并应用mvp设计思想的过程中,总结到一些问题(针对标准mvp):
1,频繁的接口调用和回调:V让P做事情,需要调用P的接口,P从M请求数据需要调用M的接口;M把数据返回给P做业务处理要调用P的回调接口,P处理完业务逻辑或者业务数据返回给V做交互反馈又要调用V的回调接口。
你会发现,一套mvp代码写完,光接口就定义了一火车,烦且繁!一个简单UI交互,动辄需要2~4个甚至更多接口定义,层级解耦的开发代价是否过大呢?
2,耦合,主要是PV的耦合,很难完全避免。V依赖P,P又要持有V的接口类型,即便网上某些设计采用了各种方式极力避免(弱引用等等) ……
那是否mvp就一定要达到一种完全解耦的效果呢?也不一定,作者从业这几年,也拜读过一些组件源码,发现一个问题,coding在封装代码的时候,往往会过度追求完美(技术流往往很执着),有时候一段很简单的代码,被设计的很复杂,层层封装,层层回调 …… 其实有时候还是适当为好吧,也要考虑开发成本,性能,代码可读性,如果是作为公共组件,还要考虑学习成本,外观接口是否便于外部调用等等因素(仅个人愚见,不喜勿喷,也许是因为作者代码能力的确有限,达不到而下意识给自己找了个借口吧)。
以最直接的方式,最简单的策略,尽可能高效滴实现需求。这是我对coding的理解。
上述浅见,作者在自己的mvp框架内,做了如下调整:
V单项依赖于P,避免P层持有V对象引用,P回馈给V通过EventBus封装消息实现。 流程就是V向P发处理命令(V调用P的方法),P做业务处理后,向Module层索要数据,M层获取数据之后接口反馈给P,P针对不同业务做数据二次加工(这个部分也可以放在M,来分担P的压力)。P将执行结果(数据,命令)封装成消息(Event),通过EventBus发送给V进行处理。(考虑到代码可读性,可追溯性,消息机制仅限于P—>V的部分,并没有用EventBus实现PV双向通讯)
综述就是,在PV之间加了EventBus消息机制,由双向依赖变成了单项依赖,保留了业务请求接口(V—>P)
(1) P层(BaseEvent)
Base Library在P层定义了Event基类,通过成员变量eventTag区分消息目的UI线程(消息应该发送给哪个View,eventTag值一般为View的包名 + 类名 字符串构成),通过成员变量actionTag区分同一UI线程不同类型的逻辑操作(登录,忘记密码,找回密码,刷新列表等操作类型)。
public abstract class BaseEvent {
/**
* default action tag for Initialization UI thread
*/
public static final String ACTION_TAG_INIT_PROCESS = "init_process";
/**
* default action tag for Http Response error
*/
public static final String ACTION_TAG_RESPONSE_ERROR = "network_response_error";
/**
* for different UI thread. default value: name of current class
*/
public String eventTag;
/**
* same UI thread, different action
*/
public String actionTag;
// …………
// …………
}
(2)V层
Base Library在V层提供了统一的Event接收入口,统一接收Event消息,并通过eventTag过滤,用户可以在UI线程通过接收到的Event的actionTag进行行为分发,区别响应。
public abstract class BaseActivity extends Activity implements Contract.IView {
// ……………………
/**
* receive Event Message
* @param event BaseEvent
* @param BaseEvent
*/
@Subscribe
@Override
public void onEventMessageReceive(E event) {
if (null == event || !event.eventTag.equals(getSimpleTag())) {
return;
}
switch (event.actionTag) {
case BaseEvent.ACTION_TAG_INIT_PROCESS:
// do sth init view
initDataView(event);
break;
default:
onHandleMessage(event);
break;
}
}
// ……………………
}
其中BaseEvent.ACTION_TAG_INIT_PROCESS定义为初始化刷新UI数据行为,单独提供消息处理接口initDataView,其余类型消息由onHandleMessage接收处理。
/**
* Init data and refresh view
* @param event BaseEvent
* @param BaseEvent
*/
protected void initDataView(E event){}
/**
* receive and handle EventBus message
* @param event BaseEvent
* @param BaseEvent
*/
protected abstract void onHandleMessage(E event);
优势:
系统内置这套BaseEvent以及接收入口,通过eventTag区分UI线程(view),actionTag区分线程内实际操作。可大大减少EventMessage(消息类)的定义数量,改善原来EventBus对每一种消息,都需要定义一种消息类的情况。结合对actionTag的统一定义和管理,简化代码的同时,也可提高代码可读性。
争议:
如果使用系统内置这套BaseEvent以及接收入口,会导致每个注册的观察者类都会收到发送的消息:
public void onEventMessageReceive(E event)
是否会影响性能呢?
作者认为微乎其微,可以忽略。策略本身实际是把原来EventBus对注册特定Message类型的观察者发送消息的点对点机制,变成了广播。但考虑本身app实际同时有效注册EventBus的类和方法数量有限,量级可控,性能影响可忽略。其实就相当于自定义了一个LocalBroadcast。
用户也可以抛弃Base Library内置的BaseEvent,在App层自定义一套EventMessage,并按照常规写法,在每个注册EventBus的类中,实现消息接收代码。同时在每个App中对Base Library的BaseActivity定义中间父类,隐藏相关方法,实现对子类的无感知。
public abstract class BaseActivity extends library.base.mvp.v.activity.BaseActivity
{
// ......
@Override
protected void initDataView(E event) {
}
@Override
protected void onHandleMessage(E event) {
}
// ......
}
增加对P的缓存管理,制定缓存策略(失效策略),有效解决由于手机横竖屏切换等原因(V重启生命周期),导致P重新创建,重新请求数据等资源消耗。(稍后详述)
/**
* cache:packageName + className(V) : Presenter
*/
private LinkedHashMap presenterCache = new LinkedHashMap<>();
目前对P的缓存是对应交互业务逻辑,是按照UI的交互逻辑划分的,也就是认为PV一 一对应(key:V的package + name; value:P),也就是实际上一个P中只注册(缓存)了一个V(eventTag)
优化目标(此处只提供建议,各位可自行优化扩展):
(1)P保持按照UI的交互逻辑划分,PV实现一对多关系,实现对P的复用(key:P的package + name; value:P),P中将注册(缓存)多个V(eventTag),P与V交互的时候,会对所有缓存的eventTag,都发送一遍EventMessage。
(2)P按照项目业务需求划分,VP实现多对多关系,实现对P的真正复用(key:P的package + name; value:P)。
契约类 Contract 来统一管理 base mvp相关接口,使得某一功能模块的接口能更加直观的呈现出来,有利于后期维护。
其中IView定义了接收系统Event消息的接口;IPresenter增加了对P生命周期接口定义;IModule定义了数据以及异常回调接口。
/**
* @author Created by qiang.hou on 2018/4/6.
* @version 1.0
*/
public interface IContract {
/**
* @author Created by qiang.hou on 2018/3/2.
* @version 1.0
*/
interface IView {
@Subscribe
void onEventMessageReceive(E event);
}
/**
* @author Created by qiang.hou on 2018/3/2.
* @version 1.0
*/
interface IPresenter {
void doInitProcess();
void onCreate();
void onStart();
void onResume();
void onPause();
void onStop();
void onSaveInstanceState(Bundle savedInstanceState);
void onDestroy();
}
/**
* @author Created by qiang.hou on 2018/5/7.
* @version 1.0
*/
interface IModule {
/**
* add
*
* @param modelClass model class
* @param modelObserver observer
*/
void addModelObserver(Class modelClass, IModelObserver modelObserver);
/**
* remove
*
* @param modelClass model class
*/
void removeModelObserver(Class modelClass);
/**
* call the notify method with model data
*
* @param modelClass model class
* @param models data
*/
void notifyModelObserver(Class modelClass, T... models);
/**
* call the notify method with model data
*
* @param modelClass model class
* @param e Exception
*/
void notifyErrorObserver(Class modelClass, E e);
/**
* @author Created by qiang.hou on 2018/5/9.
* @version 1.0
*/
interface IModelObserver {
void onNotify(T... data);
void onError(E e);
}
}
/**
* @author Created by qiang.hou on 2018/3/2.
* @version 1.0
*/
interface IPresenterManager {
BasePresenter getPresenter(String localClassPath);
void addPresenter(String localClassPath, BasePresenter presenter);
}
}
Base Library对Activity和Fragment分别作了封装,BaseActivity,BaseAppCompatActivity,BaseFragmentActivity分别对应Activity,AppCompatActivity,FragmentActivity;BaseFragment,BaseFragmentV4分别对应 android.app.Fragment,android.support.v4.app.Fragment。
UI层的封装没啥可说的,大家的做法大同小异,仅供参考。
Base类分别做了以下几件事情:
(1)把基础行为封装为模板(onCreate)
// 跟踪打印当前activity名
LogUtils.i(getSimpleTag(), "sky_dreaming on onCreate activity = "
+ getSimpleTag());
// 加载布局文件
if (returnLayoutId() > 0) {
this.setContentView(returnLayoutId());
ButterKnife.bind(this);
}
// 初始化控件
initView();
// 特殊控件设置监听器
setListener();
// 如果页面不需要在onResume生命周期中刷新数据,在onCreate中初始化加载数据;否则该行为放在onResume中执行
if (!refreshOnResume()) {
/**
* 初始化加载数据
*/
initProcess();
}
(2)提供abstract方法供子类扩展
/**
* Init views
*/
protected abstract void initView();
/**
* receive and handle EventBus message
* @param event BaseEvent
* @param BaseEvent
*/
protected abstract void onHandleMessage(E event);
/**
* get layout id
* @return layout resource id
*/
protected abstract int returnLayoutId();
/**
* create Presenter
* @return P
*/
public abstract P createPresenter();
/**
* if this view need EventBus
* @return boolean
*/
protected abstract boolean needEventBus();
(3)开放Presenter注册以及解注方法
由Application在Activity生命周期回调方法中,调用registerPresenter()完成对P的创建和绑定以及解绑
public void registerPresenter() {
if (needEventBus()) {
registerEventBus();
}
mPresenter = (P) PresenterManager.getInstance().getPresenter(getSimpleTag());
if (null == mPresenter) {
mPresenter = createPresenter();
if (null != mPresenter) {
PresenterManager.getInstance().addPresenter(getSimpleTag(), mPresenter);
}
}
if (null != mPresenter) {
mPresenter.registerViewEvent(getSimpleTag());
}
}
1)根据用户需求,选择是否注册EventBus组件
2)从缓存(PresenterManager)尝试获取presenter对象,如果获取不到,创建一个,同时将新创建的对象纳入缓存。
3)把当前UI线程的标识TAG注册给P(PV通过EventBus通讯,TAG用于区分目的UI线程)。
同时将创建P的方法createPresenter();开放给子类用户实现。
解绑:unRegisterPresenter();
public void unregisterPresenter() {
if (null != mPresenter) {
mPresenter.unRegisterViewEvent(getSimpleTag());
}
unregisterEventBus();
}
(4)开放通用行为
统一接收Event消息,并通过eventTag过滤,通过actionTag分发
@Subscribe
@Override
public void onEventMessageReceive(E event) {
if (null == event || !event.eventTag.equals(getSimpleTag())) {
return;
}
switch (event.actionTag) {
case BaseEvent. ACTION_TAG_INIT_PROCESS:
// do sth init view
initDataView(event);
break;
default:
onHandleMessage(event);
break;
}
}
获取当前activity标识(包名 + 类名)
public String getSimpleTag() {
return getPackageName() + this.getClass().getSimpleName();
}
跳转到指定页面
public void startNextActivity (Intent intent, boolean needForResult,
int requestCode, int enterAnim, int exitAnim, boolean needFinishCurrent) {
if (needForResult) {
startActivityForResult(intent, requestCode);
} else {
startActivity(intent);
}
if (enterAnim > 0 && exitAnim > 0) {
overridePendingTransition(enterAnim, exitAnim);
}
if (needFinishCurrent) {
finish();
}
}
Fragment在上述Activity封装策略的基础上,加了懒加载机制:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
/**
* (for lazy loading data)
*/
isViewInitiated = true;
prepareRequestData();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
/**
* (for lazy loading data)
*/
prepareRequestData();
}
/**
* prepare to get data (for lazy loading data)
*
* @return
*/
public boolean prepareRequestData() {
// set param true: force update data when fragment visible
return prepareRequestData(false);
}
/**
* prepare to get data (for lazy loading data)
*
* @param forceUpdate true: force update data when fragment visible
* @return
*/
public boolean prepareRequestData(boolean forceUpdate) {
if (getUserVisibleHint() && isViewInitiated && (!isDataLoaded || forceUpdate)) {
initProcess();
isDataLoaded = true;
return true;
}
return false;
}
(1)BasePresenter
Presenter对应交互业务逻辑,是按照UI的交互逻辑划分的(当前PV一对一设计)。P持有V的唯一标识(包名 + 类名)的缓存列表(当前每个P只缓存一个V的标识),通过添加,移除V标识,实现在P对V的注册解注。P通过EventBus发送带有V唯一标识的Event消息给V,V通过识别唯一标识来判断接收到的消息归属。
定义了V唯一标识的缓存列表
/**
* cache event tag
*/
private List eventTagCache = new ArrayList<>();
提供了注册,解注的开放接口
/**
* create relationship with view
* @param eventTag event tag
*/
public void registerViewEvent(String eventTag) {
if (!isViewRegistered(eventTag)) {
validTime = System.currentTimeMillis();
eventTagCache.add(eventTag);
}
}
/**
* detach the relationship
*/
public void unRegisterViewEvent(String eventTag) {
if (isViewRegistered(eventTag)) {
eventTagCache.remove(eventTag);
}
if (eventTagCache.size() == 0) {
invalidTime = System.currentTimeMillis();
}
}
并内置本身失效策略,为外部对P的缓存提供策略依据。
/**
* time for registering new event tag of view
*/
private long validTime = 0;
/**
* time for clear new event tag of view
*/
private long invalidTime = 0;
public boolean isEffective() {
return (invalidTime - validTime) / 1000 < PRESENTER_CACHE_TIME;
}
提供发送EventMessage消息接口
/**
* @param data data
* @param aTag action tag
* @param data type
*/
public void postEvent(T data, final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().post(new BaseEvent(data) {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param data data
* @param aTag action tag
* @param data type
*/
public void postStickyEvent(T data, final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().postSticky(new BaseEvent(data) {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param data data
* @param aTag action tag
* @param data type
* @param data type
*/
public void postEvent(T data, T2 data2, final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().post(new BaseEvent(data, data2) {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param data data
* @param aTag action tag
* @param data type
* @param data type
*/
public void postStickyEvent(T data, T2 data2, final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().postSticky(new BaseEvent(data, data2) {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param data data
* @param aTag action tag
* @param data type
* @param data type
* @param data type
*/
public void postEvent(T data, T2 data2, T3 data3, final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().post(new BaseEvent(data, data2, data3) {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param data data
* @param aTag action tag
* @param data type
* @param data type
* @param data type
*/
public void postStickyEvent(T data, T2 data2, T3 data3, final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().postSticky(new BaseEvent(data, data2, data3) {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param aTag action tag
*/
public void postEvent(final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().post(new BaseEvent() {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param aTag action tag
* @param E extends BaseEvent
*/
public void postStickyEvent(final String aTag) {
for (String eventTag : eventTagCache) {
final String viewTag = eventTag;
EventBus.getDefault().postSticky(new BaseEvent() {
@Override
protected String returnThreadTag() {
return viewTag;
}
@Override
protected String returnActionTag() {
return aTag;
}
});
}
}
/**
* @param event E extends BaseEvent
*/
public void postEvent(E event) {
EventBus.getDefault().post(event);
}
/**
* @param event E extends BaseEvent
*/
public void postStickyEvent(E event) {
EventBus.getDefault().postSticky(event);
}
(2)PresenterManager
P缓存管理类,内置缓存池,开放get,add接口,同时在每次get调用之前,轮询检查缓存,剔除无效缓存。
之所以设计Presenter缓存策略,主要考虑两点因素,一是对多V复用,二是节省资源,避免由于V生命周期异常等原因,导致P重构,浪费资源。之前有考虑把P的生成,P对V的注册,解注也放到管理类中实现,后来取消了,有心者可以尝试变更。
public class PresenterManager implements Contract.IPresenterManager {
/**
* cache
*/
private LinkedHashMap presenterCache = new LinkedHashMap<>();
// ......
/**
* @param localClassPath packageName + className
* @return BasePresenter
*/
public BasePresenter getPresenter(String localClassPath) {
/*
* clear Invalid presenter
*/
for (Map.Entry entry : presenterCache.entrySet()) {
if (null == entry.getKey() || null == entry.getValue()) {
presenterCache.remove(entry.getKey());
continue;
}
if (!entry.getValue().isEffective() && !entry.getKey().equals(localClassPath)) {
presenterCache.remove(entry.getKey());
}
}
if (presenterCache.containsKey(localClassPath)) {
return presenterCache.get(localClassPath);
} else {
return null;
}
}
/**
* @param localClassPath packageName + className
* @param presenter BasePresenter
*/
public void addPresenter(String localClassPath, BasePresenter presenter) {
if (null == localClassPath || null == presenter) {
return;
}
presenterCache.put(localClassPath, presenter);
}
// ......
}
Event包提供了EventMessage的基类BaseEvent,定义了eventTag,actionTag关键字段,用于区分UI线程以及业务操作,同时定义了T data,T2 data2,T3 data3增加了对Data数据的承载,用于需要传输数据的场景。
按照计划,App所有与mvp相关的消息体,都会继承BaseEvent,为UI线程统一接收消息提供支持,可以大大减少EventMessage类的定义数量,简化代码。
public abstract class BaseEvent {
/**
* default action tag for Initialization UI thread
*/
public static final String ACTION_TAG_INIT_PROCESS = "init_process";
/**
* default action tag for Http Response error
*/
public static final String ACTION_TAG_RESPONSE_ERROR = "network_response_error";
/**
* for different UI thread. default value: name of current class
*/
public String eventTag;
/**
* same UI thread, different action
*/
public String actionTag;
/**
* extra data
*/
public T data;
/**
* extra data
*/
public T2 data2;
/**
* extra data
*/
public T3 data3;
protected abstract String returnThreadTag();
protected abstract String returnActionTag();
public BaseEvent() {
init();
}
public BaseEvent(T data) {
this.data = data;
init();
}
public BaseEvent(T data, T2 data2) {
super();
this.data = data;
this.data2 = data2;
init();
}
public BaseEvent(T data, T2 data2, T3 data3) {
super();
this.data = data;
this.data2 = data2;
this.data3 = data3;
init();
}
private void init() {
eventTag = returnThreadTag() == null ? getClass().getPackage() + getClass().getSimpleName() : returnThreadTag();
actionTag = returnActionTag() == null ? ACTION_TAG_INIT_PROCESS : returnActionTag();
}
}
Module对应业务数据管理,是按照数据业务划分的(一个Module对应并管理多个Model(数据Bean,可以理解为接口,网络接口,数据库接口等))。
无论对于功能型mvp架构(mvp作为顶层节点,每个M,V,P中进行业务模块划分)而言,还是对于业务型mvp架构(业务模块作为顶层节点,每个业务节点中做mvp层次划分)而言,Module就是P的数据管理员(按业务模块划分),P管理业务,Module管理数据,至于Module把业务中涉及到的数据(Model)存到remote还是local,P并不关心。Module管理当前业务模块中各个Model(Bean)的存储;Repository负责对数据在对象与存储介质之间进行校验,封装,转换(可有可无)。
结构说明:P ——> Module ->(Repository ->)(LoginModel –> net,RegisterModel -> local)
Module将被设计为数据存储层(Model)向上层业务逻辑层(Presenter)提供服务的入口(外观)。并负责协调Data Repository,管理并加工业务数据。
Base Library对Module的封装比较简单,按照MP以接口调用及回调的设计思想,BaseModule做了如下封装:
(1)缓存数据回调Observers
/**
* model callback observer
*/
private Map modelChangeObserver;
说明一下:数据回调的Observer是针对Model(数据)的,不是针对P的,所以一个P中可能会向Module注册多个Observer用于接收不同的类型的Model。
(2)对P回调接口的注册解注:
/**
* add
*
* @param modelClass model class
* @param modelObserver observer
*/
public BaseModule addModelObserver(Class modelClass, IModelObserver modelObserver) {
if (null == modelChangeObserver) {
modelChangeObserver = new HashMap<>();
}
if (!modelChangeObserver.containsKey(modelClass)) {
modelChangeObserver.put(modelClass, modelObserver);
}
return this;
}
/**
* remove
*
* @param modelClass model class
*/
public void removeModelObserver(Class modelClass) {
if (modelChangeObserver.containsKey(modelClass)) {
modelChangeObserver.remove(modelClass);
}
}
P中初始化Module时,根据P负责处理的业务,把对应不同Model(数据)的回调接口注册给Module。
(3)按照Model类型做数据以及异常回调
@SuppressWarnings("unchecked")
@Override
public void notifyModelObserver(Class modelClass, T... models) {
if (modelChangeObserver.containsKey(modelClass)) {
modelChangeObserver.get(modelClass).onNotify(models);
}
}
@SuppressWarnings("unchecked")
@Override
public void notifyErrorObserver(Class modelClass, E e) {
if (modelChangeObserver.containsKey(modelClass)) {
modelChangeObserver.get(modelClass).onError(e);
}
}
Module子类中,针对业务需求扩展获取Model(数据)的方法,获取Model(数据)后,调用notifyModelObserver方法把数据回传给P。
(4)定义数据回调接口
/**
* @author Created by qiang.hou on 2018/5/9.
* @version 1.0
*/
public interface IModelObserver {
void onNotify(T... data);
}
静态全局参数,默认配置参数等,不做赘述
public class GlobalField {
public static final String SHARED_PREFERENCE_NAME = "default_shared_preference";
public static final long PRESENTER_CACHE_TIME = 20;
}
Base Library是一个android库,也是一个完整的android工程,其他主工程app1,app2,app3需要添加Base Library工程依赖。
mvp是一种设计思想,致力于层级解耦,但它也只是一种架构思想,具体实现不应被mvp固定模式所束缚。
Base Library可优化的方向(仅供参考)
1,按照业务逻辑而非UI逻辑划分P,实现PV多对多,实现对P的充分复用。
2,Module与Model之间,增加Repository,用于对原始数据(remote,localDB)的业务性加工处理。
3,放大EventBus消息机制的使用范围,目前只用于P—>V业务回调部分。
最后,好抢手是子弹喂出来的,好coding是代码练出来的,Lib中涉及到的关键代码(基本上是所有)都已贴出,真正有兴趣的不妨整理优化一下,欢迎交流心得体会。
源码:https://gitee.com/sky_dreaming/App_M/tree/library/library_base