三年前写过一段时间Android的代码,后面又开始做一些跨平台的工作,比如之前的react-native ,现在的flutter 等,现在想重新找一份工作,思虑再三,还是把Android 捡一捡。

废话不多说,先封装个base MVP 框架记录一下
简单的介绍一下MVP思想,它是将View 层与Model 层彻底隔离,意味着View 和 Model 都不再持有对方的引用,它们通过一个叫做Presenter 的第三者来代理事务的传递,所有Presenter 层会持有Model 与View 层的引用。如下图

Android 封装开发框架mvp 安卓mvp封装_Android 封装开发框架mvp

我们访问网络得到数据并显示出来,会是这样的一个流程
1、Activity 启动时,告诉presenter 我要数据了。
2、Presenter 就会叫Model 去访问数据接口,获取数据。
3、Model 得到数据后,返回给Presenter 了,Presenter 一看数据不规范,赶紧处理一下,处理完成
4、Presenter 把处理后的数据汇报给了activity(View), View 拿到数据后,就去做显示的操作,MVP 流程走完,工作结束。

看到这里,MVP 流程是走完了,我们的工作却刚开始,下面一起来实现一下整个流程。

先看一下项目文件图

Android 封装开发框架mvp 安卓mvp封装_ci_02

既然是写MVP,很多新学朋友肯定不知道先从哪里下手去写,那么既然是MVP,我们就先从Model 层开始吧。

1、Model
新建一个base 文件,在里面创建一个BaseModel
这里的其实就是一个空的类,后期的话,我们根据项目需求,再添加内容

package com.traveleasy.electricity.Base;

public abstract class BaseModel {
    
}

2、View 层
我们的IBaseView 是一个接口类,这里现在就只干一件事情,获取上下文

package com.traveleasy.electricity.Base;

import android.content.Context;

public interface IBaseView {

    Context getContext();

}

看到这里,很多人会问,你的View 层就这些吗?答案肯定不是啦,各位看官,请别着急,视图层,我在后面会写。

3、presenter
重点来了,其实在我个人看来,整个MVP 框架中,presenter算是最辛苦的了,因为它要统筹大局,调兵遣将不是。

我先创建一个 IBasePresenter 接口类,这里我们定义一个IBaseView 的泛型,里面定义两个方法,绑定view 和解绑view 。

package com.traveleasy.electricity.Base;

public interface IBasePresenter<V extends IBaseView> {

    // 绑定
    void attachView(V view);
    // 解绑
    void detechView();
}

下面是重点,BasePresenter 的实现类
这这里,我使用了软引用的方式,来处理presenter 在获取activity 和销毁的时候,造成的内存泄漏的问题。
也运用了一些aop 的思想,即通过动态代理,做统一的逻辑判断。

package com.traveleasy.electricity.Base;

import androidx.lifecycle.LifecycleObserver;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

/**
 * 使用软引用的方式让 P 层持有 V 层的引用,并且提供了 getView() 方法给 P 层调用,
 * 父类 View 变量进行私有化,防止子类对其进行更改造成的其他错误。我们的 MainPresenter
 * 获取 Activity 的引用就可以使用 getView() 方法获得。软引用在内存降到不足的情况下,
 * GC 就会进行优先回收释放那些以软引用方式引用的对象,一定程度上去避免内存溢出(OOM)。
 *
 * @param <V>
 */
public abstract class BasePresenter<V extends IBaseView, M extends BaseModel> implements IBasePresenter {

    //    使用软引用,避免内存泄漏,导致OOM 情况发送
    protected SoftReference<IBaseView> mReferenceView;
    protected V mProxyView;
    protected M mModel;

    @SuppressWarnings("unchecked")
    @Override
    public void attachView(IBaseView view) {
//        使用软引用创建对象
        mReferenceView = new SoftReference<>(view);
//       通过使用动态代理,做统一的逻辑判断,aop 思想
        mProxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(), view.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

                if (mReferenceView == null || mReferenceView.get() == null) {
                    return null;
                }

                return method.invoke(mReferenceView.get(), objects);
            }
        });
//        通过获得泛型类的父类,拿到泛型的接口实例,通过反射来实例化 model
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        if (type != null) {
            Type[] types = type.getActualTypeArguments();
            try {
                mModel = (M) ((Class<?>) types[1]).newInstance();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    @SuppressWarnings("unchecked")
    public V getView() {
        return mProxyView;
    }

    @SuppressWarnings("unchecked")
    public M getModel() {
        return mModel;
    }

    @Override
    public void detechView() {
        mReferenceView.clear();
        mReferenceView = null;
    }
}

由于Java 单继承的特性,会造成我们在封装BaseActivity 和BaseFragment 时,会有很多的重复代码,BaseActivity 必须继承 Activity 才能启动,而 BaseFragment 又必须继承 Fragment 。

在讲解BaseActivity和BaseFragment 之前,我们先看一下代理模块的代码。

1、新建一个Proxy 接口类

package com.traveleasy.electricity.proxy;

/**
 * 由于Java 单继承的特性,这里我们使用proxy 代理,来实现 BaseActivity 和 BaseFragment 重复代码的封装实现
 * 这里封装两个接口,一个绑定Presenter, 一个解绑Presenter
 */
public interface IProxy {

    void bindPresenter();

    void unBindPresenter();
}

2、创建proxyIml 实现类

package com.traveleasy.electricity.proxy;

import com.traveleasy.electricity.Base.BasePresenter;
import com.traveleasy.electricity.Base.IBaseView;
import com.traveleasy.electricity.inject.InjectPresenter;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 前面我们创建了proxy 代理,里面有解绑和绑定Presenter 的抽象方法
 * 这里我们创建一个proxyIml 接口实现类,用来统一代理重复的代码
 */
public class ProxyImpl implements IProxy {

    private IBaseView mView;
    //    定义一个数组,保存使用过的presenter,用于解绑
    private List<BasePresenter> mInjectPresenters;

    public ProxyImpl(IBaseView view) {
        this.mView = view;
        mInjectPresenters = new ArrayList<>();
    }

    /**
     * 绑定 presenter 的实现
     */
    @Override
    public void bindPresenter() {
//        获得已经声明的变量,包括私有的
        Field[] fields = mView.getClass().getDeclaredFields();
        for (Field field : fields) {
//            获取变量上面的注解类型
            InjectPresenter injectPresenter = field.getAnnotation(InjectPresenter.class);
            if (injectPresenter != null) {

                try {

                    Class<? extends BasePresenter> type = (Class<? extends BasePresenter>) field.getType();
                    BasePresenter mInjectPresenter = type.newInstance();
                    mInjectPresenter.attachView(mView);
                    field.setAccessible(true);
                    field.set(mView, mInjectPresenter);
                    mInjectPresenters.add(mInjectPresenter);

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                    throw new RuntimeException("SubClass must extends Class:BasePresenter");
                }
            }
        }
    }

    /**
     * 解绑 presenter 的实现
     * 避免内存泄漏
     */
    @Override
    public void unBindPresenter() {
        for (BasePresenter presenter : mInjectPresenters) {
            presenter.detechView();
        }

        mInjectPresenters.clear();
        mInjectPresenters = null;
    }
}

3、再分别创建proxyActivity和 proxyFragment 类

package com.traveleasy.electricity.proxy;

import com.traveleasy.electricity.Base.IBaseView;

/**
 * 新建proxyActivity 代理实现类,用来代理activity 中重复的代码
 */
public class ProxyActivity<V extends IBaseView> extends ProxyImpl {


    public ProxyActivity(IBaseView view) {
        super(view);
    }
}
package com.traveleasy.electricity.proxy;

import com.traveleasy.electricity.Base.IBaseView;

/**
 * 新建proxyFragment 实现类,用于处理fragment 中重复的代码
 */
public class ProxyFragment<V extends IBaseView> extends ProxyImpl {

    public ProxyFragment(IBaseView view) {
        super(view);
    }
}

这里的两个代理类暂时没有代码,因为我们还没有需要在里面处理的业务逻辑,不过我们必须得传入一个泛型的IBaseView 对象,这里的原因就是我们的 ProxyImpl 类中的 presenter 调用 attach() 方法去绑定 View 时,这个 View 是继承 IBaseView 的,所以这必须要一个参数给它,通过继承 ProxyImpl 类将这个 view 用构造函数的方式传给父类。

接下来我们来看一下视图中的代码

package com.traveleasy.electricity.Base;

import android.content.Context;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.traveleasy.electricity.inject.InjectPresenter;
import com.traveleasy.electricity.proxy.ProxyActivity;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public abstract class BaseActivity extends AppCompatActivity implements IBaseView {

    private ProxyActivity mProxyActivity;

    protected abstract void initLayout(@Nullable Bundle savedInstanceState);

    protected abstract void initViews();

    protected abstract void initData();


    @SuppressWarnings("SamePresenterValue")
    protected <T extends View> T $(@IdRes int viewId) {
        return findViewById(viewId);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        initLayout(savedInstanceState);

        mProxyActivity = createProxyActivity();
        mProxyActivity.bindPresenter();

        initViews();
        initData();
    }

    @SuppressWarnings("unchecked")
    private ProxyActivity createProxyActivity(){

//        代理为null 的时候, 重新初始化
        if (mProxyActivity == null){
            return new ProxyActivity(this);
        }

        return mProxyActivity;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        mProxyActivity.unBindPresenter();
    }

    @Override
    public Context getContext() {
        return this;
    }

}
package com.traveleasy.electricity.Base;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.traveleasy.electricity.inject.InjectPresenter;
import com.traveleasy.electricity.proxy.ProxyFragment;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public abstract class BaseFragment extends Fragment implements IBaseView {

    private ProxyFragment mProxyFragment;

    protected abstract @LayoutRes
    int setLayout();

    protected abstract void initViews(@Nullable Bundle savedInstanceState);

    protected abstract void initData();

    @SuppressWarnings("ConstantConditions")
    protected <T extends View> T $(@IdRes int viewId) {
        return this.getView().findViewById(viewId);
    }

    @SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = inflater.inflate(setLayout(), container, false);

        mProxyFragment = createProxyFragment();
        mProxyFragment.bindPresenter();

        return view;
    }

    private ProxyFragment createProxyFragment(){

//        如果代理为null 的话,就重新初始化
        if (mProxyFragment == null){
            return new ProxyFragment(this);
        }

        return mProxyFragment;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initViews(savedInstanceState);
        initData();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

       mProxyFragment.unBindPresenter();
    }
}

上面BaseActivity 和BaseFragment 中的代码是不是很简单?
我们通过代理的方式,代理了View 的绑定和解绑的相关代码。

以上就是BaseMvp 框架的封装。

BaseMvp 框架的使用

源码下载