疫情刚好静下心来回归过去的工作,工作以后往往都是往前跑,其实偶尔多回归下技术,沉淀下来,这是博主抽离的mvp架构,本章源码linkhttps://github.com/apple317/AppleMvp,分为mvp_java和mvp_kotlin两个分支版本,网络组件com.applehsp.http:AppleHttpJava:1.0.0,mvp地址依懒com.applehsp.mvp:MvpKotlin:1.0.7,组件化demo例子请看添加链接描述,源demo源码添加链接描述,需要请评论联系博主,麻烦动动手点个星,万分感谢。
1.支持rxjava、RxLifecycle特性
2.支持加载中过渡扩展
3.支持java、kotlin双版本mvp
4.支持多个Presenter共用
5.插拔式的AOP设计理念
6.支持fragement、activity调用
7.支持依懒引入
8.支持androidx、android两个版本
Mvp
1.Mvp使用说明
1.1 java-mvp库依懒说明
第一步引入依懒,本库已发到jenter中心,请记得在根工程中添加jenter():
第二步添加依懒java-mvp版本库
implementation 'com.applehsp.mvp:MvpJava:1.0.7'
1.2 java-mvp使用说明
public class SplashPresenter extends BasePresenter<SplashView> {
public SplashPresenter() {
}
/**
* 广告方法
* @param message
*/
public void advertList(String message) {
getView().advertList(message);
}
}
public interface SplashView extends IBaseView {
void advertList(String advert);
}
public class MainActivity extends BaseMvpActivity implements SplashView, MainView{
@BindView(R.id.btnSync)
Button btnSync;
@BindView(R.id.btnAdvert)
Button btnAdvert;
//通过注释,通过运行时动态代理p-->v关联,支持多个p注入
@CreatePresenterAnnotation(SplashPresenter.class)
SplashPresenter splashPresenter;
@CreatePresenterAnnotation(MainPresenter.class)
MainPresenter mainPresenter;
@Override
public int setLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initData() {
}
@Override
protected void initView(Bundle savedInstanceState) {
ButterKnife.bind(this);
}
@Override
public void appSync(String msg) {
btnSync.setText(msg);
}
@Override
public void advertList(String advert) {
btnAdvert.setText(advert);
}
@OnClick({R.id.btnSync, R.id.btnAdvert})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btnSync:
mainPresenter.appSync("首页初始化");
break;
case R.id.btnAdvert:
splashPresenter.advertList("启动页初始化");
break;
}
}
}
1.3 kotlin版本库依懒说明
第一步引入依懒,本库已发到jenter中心,请记得在根工程中添加jenter():
第二步添加依懒java-kotlin版本库
implementation 'com.applehsp.mvp:MvpKotlin:1.0.0'
```java
## 1.4 kotlin版本库使用说明
```kotlin
class SplashPresenter : BasePresenter<SplashView>() {
/**
* 广告方法
* @param message
*/
fun advertList(message: String) {
view!!.advertList(message)
}
}
interface SplashView : IBaseView {
fun advertList(advert: String)
}
class MainActivity : BaseMvpActivity(), SplashView, MainView {
@CreatePresenterAnnotation(SplashPresenter::class)
var splashPresenter: SplashPresenter? = null
@CreatePresenterAnnotation(MainPresenter::class)
var mainPresenter: MainPresenter? = null
override fun setLayoutId(): Int {
return R.layout.activity_main
}
override fun initData() {
btnAdvert.setOnClickListener { splashPresenter?.advertList("kotlin 广告") }
btnSync.setOnClickListener { mainPresenter?.appSync("kotlin 同步") }
}
override fun appSync(msg: String) {
btnSync.text = msg
}
override fun advertList(advert: String) {
btnAdvert.text = advert
}
override fun initView(savedInstanceState: Bundle?) {
}
}
class HomeFragment : BaseMvpFragmentX() {
override fun initView() {}
override fun initData(savedInstanceState: Bundle?) {
}
override fun showProgress(loading: Boolean) {
}
override fun onResume() {
super.onResume()
}
override fun setLayoutId(): Int {
return R.layout.hot
}
}
class UserFragment : BaseMvpFragment(){
override fun initData() {
}
override fun initView() {
}
override fun setLayoutId(): Int {
return R.layout.activity_main
}
}
两个版本完全调用写法一样,保证业务层低转化成本,p层方法可以自由定义扩张,只需要定义view的回传的方法,p层耦合很低,我们可以在开发业务逻辑最小颗粒化,而代码量很少,在下面也会对比几个mvp,么有对比就么有优势和伤害。
2.Mvp原理讲解
在这里开篇首先说一下,每个架构其实原理核心代理不会超过1000代码,这里只讲解原理核心思想,欢迎大家一起集思广益,多多点星,多多分享,博主马上会研究flutter,会赚到flutter插件研发,如果要想封装一个库分享,最好先github找到对应的库看个三四个,这样对你帮助真的很大,这是博主好多年的心得。
2.1 java-mvp原理讲解
这里使用是注解,运行时的获取对应属性方法和值,然后通过反射注释类相关对应BasePresenter,然后newInstance实力化对象,Presenter和view必须要弱引用,例如:
页面销毁,网络请求可能在进行中,那么问题就来了会导致空指针到处崩溃,怎么解决这个疼点,就要取消这两个关联。
rxjava怎么办,这个也会崩溃,放心这个博主想到了,支持rxjava、支持rxlifecycle3版本特性。
@Retention(RUNTIME)
@Target(FIELD)
public @interface CreatePresenterAnnotation {
Class<? extends BasePresenter> value();
}
/**
* 支持rxjava,绑定生命周期
* 预留支持加载过渡效果实现
* @return
*/
@Override
public <T> LifecycleTransformer<T> bindLifeycle(boolean loading) {
if (loading) {
showProgress();
}
return this.bindToLifecycle();
}
//通过弱引用来存储view层对象。
private WeakReference<V> mWeakReference;
private V mProxyView;
/**
* 关联V层和P层
*
*/
public void attatchView(V v) {
mWeakReference = new WeakReference<>(v);
MvpViewHandler viewHandler = new MvpViewHandler(mWeakReference.get());
//这里重点核心v通过动态代理实例化view对象
mProxyView = (V) Proxy.newProxyInstance(v.getClass().getClassLoader(), v.getClass().getInterfaces(), viewHandler);
}
/**
* 断开V层和P层
* 在Acitivity的onDestory()中调用
*/
public void detachView() {
if (isViewAttached()) {
mWeakReference.clear();
mWeakReference = null;
}
}
//这里通过工厂设计模式,通过反射机制实例化对应Presenter对象。
public static <V extends IBaseView, P extends BasePresenter<V>> MvpPresenterFactroyImpl creater(Context context) {
//拿到创建presenter的注解
List<BasePresenter> currentPresenter = new ArrayList<>();
Log.e("HU","MvpPresenterFactroyImpl=creater==="+context);
try{
Field[] fields = context.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(CreatePresenterAnnotation.class)) {
Log.e("HU","MvpPresenterFactroyImpl=11111==="+fields);
CreatePresenterAnnotation address = field.getAnnotation(CreatePresenterAnnotation.class);
Class<? extends BasePresenter> presenter = address.value();
try {
field.setAccessible(true); //设置属性值的访问权限
BasePresenter presenter1 = presenter.newInstance();
field.set(context, presenter1); //将查找到的view指定给目标对象object
currentPresenter.add(presenter1);
} catch (IllegalAccessException e) {
Log.e("HU","MvpPresenterFactroyImpl=IllegalAccessException==="+context);
throw new RuntimeException(e);
} catch (InstantiationException e) {
Log.e("HU","MvpPresenterFactroyImpl=InstantiationException==="+context);
e.printStackTrace();
}
}
}
}catch (Exception e){
Log.e("HU","MvpPresenterFactroyImpl=2222==="+context);
e.printStackTrace();
}
return new MvpPresenterFactroyImpl(currentPresenter);
}
2.2 kotlin-mvp原理讲解
//请在https://github.com/apple317/AppleMvp代码库下找MvpPresenterFactroyImpl.kt文件
fun mvpCreate(context: Context): MvpPresenterFactroyImpl{
//拿到创建presenter的注解
val currentPresenter: ArrayList<BasePresenter<out IBaseView>> = ArrayList<BasePresenter<out IBaseView>>()
Log.e("HU", "MvpPresenterFactroyImpl=creater===$context")
try {
val fields = context.javaClass.declaredFields
for (field in fields) {
if (field.isAnnotationPresent(CreatePresenterAnnotation::class.java)) {
Log.e("HU", "MvpPresenterFactroyImpl=11111===$fields")
val address = field.getAnnotation(CreatePresenterAnnotation::class.java)
val presenter: KClass<out BasePresenter<out IBaseView>> = address.value
try {
field.isAccessible = true //设置属性值的访问权限
val presenter1: BasePresenter<out IBaseView> = presenter.java.newInstance()!!
field[context] = presenter1 //将查找到的view指定给目标对象object
currentPresenter.add(presenter1)
} catch (e: IllegalAccessException) {
Log.e("HU", "MvpPresenterFactroyImpl=IllegalAccessException===$context")
throw RuntimeException(e)
} catch (e: InstantiationException) {
Log.e("HU", "MvpPresenterFactroyImpl=InstantiationException===$context")
e.printStackTrace()
}
}
}
} catch (e: Exception) {
Log.e("HU", "MvpPresenterFactroyImpl=2222===$context")
e.printStackTrace()
}
return MvpPresenterFactroyImpl(currentPresenter)
}
@Suppress("UNCHECKED_CAST")
fun attatchView(v: IBaseView) {
weak.weakReference=WeakReference(v)
val viewHandler = weak.weakReference.get()?.let { MvpViewHandler(it) }
view = Proxy.newProxyInstance(this::class.java.classLoader, v.javaClass.interfaces,viewHandler)as V
}
```kotlin
private inner class MvpViewHandler internal constructor(private val mvpView: IBaseView) : InvocationHandler {
@Throws(Throwable::class)
override fun invoke(proxy: Any, method: Method, args: Array<Any>): Any? { //解决第三个问题,如果V层没被销毁, 执行V层的方法.
try {
if (isViewAttached) {
return method.invoke(mvpView, *args)
}
} catch (e: InvocationTargetException) {
throw e.cause!!
}
//P层不需要关注V层的返回值
return null
}
}
对比java和kotlin是不是很相似,虽然语法、特性不一样,但是实现思路是一样的,AOP的原理就是Java的动态代理机制,我们再也不需要关心业务层膨胀,而需要更改多处代码,而通过创建一个代理对象,业务层调用方法也是代理对象方法,真正做到解耦合。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
3.Mvp对比伤害
3.1 第一对比例子
class HotTabPresenter:BasePresenter<HotTabContract.View>(),HotTabContract.Presenter {
private val hotTabModel by lazy { HotTabModel() }
override fun getTabInfo() {
checkViewAttached()
mRootView?.showLoading()
val disposable = hotTabModel.getTabInfo()
.subscribe({
tabInfo->
mRootView?.setTabInfo(tabInfo)
},{
throwable->
//处理异常
mRootView?.showError(ExceptionHandle.handleException(throwable), ExceptionHandle.errorCode)
})
addSubscription(disposable)
}
}
interface HotTabContract {
interface View:IBaseView{
/**
* 设置 TabInfo
*/
fun setTabInfo(tabInfoBean: TabInfoBean)
fun showError(errorMsg:String,errorCode:Int)
}
interface Presenter:IPresenter<View>{
/**
* 获取 TabInfo
*/
fun getTabInfo()
}
}
class SearchActivity : BaseActivity(), SearchContract.View {
private val mPresenter by lazy { SearchPresenter() }
init {
mPresenter.attachView(this)
//细黑简体字体
mTextTypeface = Typeface.createFromAsset(MyApplication.context.assets, "fonts/FZLanTingHeiS-L-GB-Regular.TTF")
}
}
3.2 第二对比例子
interface MainMVPPresenter<V : MainMVPView, I : MainMVPInteractor> : MVPPresenter<V, I> {
fun refreshQuestionCards(): Boolean?
fun onDrawerOptionAboutClick() : Unit?
fun onDrawerOptionRateUsClick(): Unit?
fun onDrawerOptionFeedClick(): Unit?
fun onDrawerOptionLogoutClick()
}
class MainPresenter<V : MainMVPView, I : MainMVPInteractor> @Inject internal constructor(interactor: I, schedulerProvider: SchedulerProvider, disposable: CompositeDisposable) : BasePresenter<V, I>(interactor = interactor, schedulerProvider = schedulerProvider, compositeDisposable = disposable), MainMVPPresenter<V, I> {
override fun onAttach(view: V?) {
super.onAttach(view)
getUserData()
getQuestionCards()
}
override fun refreshQuestionCards() = getQuestionCards()
override fun onDrawerOptionRateUsClick() = getView()?.openRateUsDialog()
override fun onDrawerOptionFeedClick() = getView()?.openFeedActivity()
override fun onDrawerOptionAboutClick() = getView()?.openAboutFragment()
override fun onDrawerOptionLogoutClick() {
getView()?.showProgress()
interactor?.let {
compositeDisposable.add(
it.makeLogoutApiCall()
.compose(schedulerProvider.ioToMainObservableScheduler())
.subscribe({
interactor?.performUserLogout()
getView()?.let {
it.hideProgress()
it.openLoginActivity()
}
}, { err -> println(err) }))
}
}
private fun getQuestionCards() = interactor?.let {
compositeDisposable.add(it.getQuestionCardData()
.compose(schedulerProvider.ioToMainSingleScheduler())
.subscribe({ questionCard ->
getView()?.let {
if (questionCard.isEmpty()) return@subscribe
else it.displayQuestionCard(questionCard)
}
}, { err -> println(err) }))
}
private fun getUserData() = interactor?.let {
val userData = it.getUserDetails()
getView()?.inflateUserDetails(userData)
}
}
class MainActivity : BaseActivity(), MainMVPView, NavigationView.OnNavigationItemSelectedListener,
HasSupportFragmentInjector {
@Inject
internal lateinit var presenter: MainMVPPresenter<MainMVPView, MainMVPInteractor>
@Inject
internal lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
}
interface MainMVPView : MVPView {
fun inflateUserDetails(userDetails: Pair<String?, String?>)
fun displayQuestionCard(questionCard: List<QuestionCardData>)
fun openLoginActivity()
fun openFeedActivity()
fun openAboutFragment()
fun openRateUsDialog(): Unit?
fun lockDrawer(): Unit?
fun unlockDrawer(): Unit?
}