Android MVP Base类的封装,内存泄漏,代理,多presenter,注解依赖等封装

举报
半身风雪 发表于 2022/06/30 10:18:47 2022/06/30
【摘要】 三年前写过一段时间Android的代码,后面又开始做一些跨平台的工作,比如之前的react-native ,现在的flutter 等,现在想重新找一份工作,思虑再三,还是把Android 捡一捡。废话不多说,先封装个base MVP 框架记录一下简单的介绍一下MVP思想,它是将View 层与Model 层彻底隔离,意味着View 和 Model 都不再持有对方的引用,它们通过一个叫做Pres...

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

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

在这里插入图片描述

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

看到这里,MVP 流程是走完了,我们的工作却刚开始,下面一起来实现一下整个流程。
先看一下项目文件图
在这里插入图片描述

既然是写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 框架的使用

源码下载

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。