Android MVP 运用反射配置泛型 Model

作者: Android架构师丨小熊 | 来源:发表于2019-08-11 22:21 被阅读6次

    这篇是基于上篇(Android MVP 架构 MVP 内存泄漏分析与动态代理)的 BaseMVP 框架基础上进行配置和修改的,上篇我们介绍了 MVP 可能存在的内存泄漏的问题,还有就是如何使用动态代理,我们把同一段代码或同一个业务逻辑判断操作项抽离出来,用的就是 AOP 思想。AOP 思想的一种编程手段,使用动态代理的方式进行抽离重复的代码或进行统一的逻辑处理。

    使用动态代理对 View 层进行抽离统一的逻辑判断,这一项工作我们在上篇文章中已经完成了。紧接着,我们就应该考虑其他层的封装处理。这篇文章,我们就拿 Model 层开刀。

    首先,看看我们的 BaseMVP 框架中的 Model 层,Model 虽然提供数据源都各不相同,但每次引用它时,都需要在 Presenter 层拿到它的引用才行,最简单的引用方式是 new 出它的实例,比如下面的 MianPresenter 中的代码:

    /**
     * presenter 层,承担业务逻辑处理,数据源处理等
     */
    public class MainPresenter extends BasePresenter<MainContract.IMainView> implements MainContract.IMainPresenter {
     
        private MainContract.IMainModel mModel;
     
        @Override
        public void attech(IBaseView view) {
            super.attech(view);
            mModel = new DataModel();
        }
     
        @Override
        public void handlerData() {
            getView().showDialog();
     
            mModel.requestBaidu(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                }
     
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    String content = response.body().string();
                    getView().succes(content);
                }
            });
        }
     
        @Override
        public void detech() {
            super.detech();
            /**
             * 释放内存、关闭网络请求、关闭线程等操作
             */
            Log.d("==========", "detech: 解除绑定,释放内存");
        }
    }
    

    就像代码中的 IMainModel 接口,我们每次要获取数据源时,都要去 new 它的一个实现类,这种方法都是可以的。但是,每次去 new 又显得麻烦,而且通常来说,一个 Presenter 和一个 Model 是一对一的关系,所以,我们想到了创建对象的另一种方式:通过反射来获取。

    首先,分析一下每一个 Model 肯定是不同的类型,这里就必须用到泛型。又因为 Model 层只有与 Presenter 层才有引用的关系,Presenter 持有 Model 的引用,所以这里的 Presenter 所持有的必定是一个泛型的 Model ,而不是具体的。而且我们是写 BaseMVP 框架,所以泛型应该封装到 BasePresenter 基类中去,才能让实现它的子类去调用。

    于是呢,我们的第一步,创建 BaseModel 基类,这里没有什么新的方法,主要用于泛型与继承的关系。如果有什么比较特殊的数据源或通用的可以在基类中提供。

    第4个版本我们与前3个版本对比,就添加了一个 BaseModel 类,来看看包详情

    创建 BaseModel 基类

    package com.test.mvp.mvpdemo.mvp.v4.basemvp;
     
    public abstract class BaseModel {
    }
    

    第二步,我们的 DataModel 就要继承 BaseModel 基类,提供相应的 Presenter 提供特有的数据。

    修改 DataModel 实现类

    package com.test.mvp.mvpdemo.mvp.v4.model;
     
    import com.test.mvp.mvpdemo.mvp.v4.MainContract;
    import com.test.mvp.mvpdemo.mvp.v4.basemvp.BaseModel;
     
    import okhttp3.Callback;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
     
    /**
     * model 层,请求网络或数据库,提供数据源(原始数据)
     */
    public class DataModel extends BaseModel implements MainContract.IMainModel {
     
        @Override
        public void requestBaidu(Callback callback) {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url("https://www.baidu.com/")
                    .build();
            client.newCall(request).enqueue(callback);
        }
    }
    

    接下来,就是我们的重点:BasePresenter 类的修改与处理,我们要为 BasePresenter 类再添加一个泛型参数,提供对不同 Model 实现类的支持。再者,就是使用反射来实例化 Model 对象,这里可能是比较容易出错的。一个是对反射的理解,另一个是对泛型的理解。我们先看看代码,再进行解释。

    修改 BasePresenter 基类

    package com.test.mvp.mvpdemo.mvp.v4.basemvp;
     
    import android.util.Log;
     
    import java.lang.ref.SoftReference;
    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;
     
    public abstract class BasePresenter<V extends IBaseView, M extends BaseModel> implements IBasePresenter {
        private SoftReference<IBaseView> mReferenceView;
        private V mProxyView;
        private M mModel;
     
        @SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
        @Override
        public void attach(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;
        }
     
        protected M getModel() {
            return mModel;
        }
     
        @Override
        public void detach() {
            mReferenceView.clear();
            mReferenceView = null;
        }
    }
    

    先看类的参数,我们添加了一个继承自刚刚写的 BaseModel 类的泛型参数,这里不难理解。可能比较难理解的是如何实例化 Model 对象吧,也就是这个例子中的 DataModel 类的实例化。

    既然这样,我们先来看看在 MainPresenter 实现类中该如何传递这个泛型 Model ,应该是传入 DataModel 吧,来看看我们的MainPresenter 类代码:

    修改 MainPresenter 实现类

    /**
     * presenter 层,承担业务逻辑处理,数据源处理等
     */
    public class MainPresenter extends BasePresenter<MainContract.IMainView, DataModel> implements MainContract.IMainPresenter {
     
        @Override
        public void handlerData() {
            getView().showDialog();
     
            getModel().requestBaidu(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                }
     
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    String content = response.body().string();
                    getView().succes(content);
                }
            });
        }
     
        @Override
        public void detach() {
            super.detach();
            /**
             * 释放内存、关闭网络请求、关闭线程等操作
             */
            Log.d("==========", "detech: 解除绑定,释放内存");
        }
    }
    

    这里我们传入的是 DataModel 的实例,而 MainPresenter 类是继承自 BasePresenter 基类的,所以在 BasePresenter 的反射代码中的 this 指的就是 MainPresenter 类的对象。看如下反射代码:

    反射代码部分

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

    通过 Log 打印就能验证一下,看看是否正确。

    我们拿到的是 MainPresenter 类的对象,这不是我们想要的啊,我们想要的是 DataModel 类的对象。又因为这里 DataModel 是通过 BasePresenter 类的泛型传进去的,所以我们通过反射机制是可以获取到 MainPresenter 的父类 BasePresenter 类。再接着,我们应该去获取 BasePresenter 的泛型参数,这里的 BasePresenter 有两个泛型参数,它返回一个 Type[] 数组。而第二个就是我们需要的真正的 DataModel 类对象了,最后通过类对象的 newInstance() 实例化就可以了。

    看起来是有点难理解,不过还是 Java 的基础啊,正是运用了 Java 的反射来动态的创建 Model 层,这是我们写框架的必备知识。那么,到这里我们又解决了 Model 层需要手动 new 来实例化的问题,通过反射可以让我们的代码变得更加的优雅,看起来也比较舒服。当然,对于初学者来说,这样的代码看起来是有点难理解,还是要死磕到底啊。

    最后
    最后我准备了一些面试的知识汇总,数据结构,计算机网络等等都有。自己整理和分类的,还请尊重知识产出。
    分享给大家的资料包括高级架构技术进阶脑图、Android开发面试专题资料,还有高级进阶架构资料包括但不限于【高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术】希望能帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也是可以分享给身边好友一起学习的!
    资料免费领取方式:私信回复“架构资料”即可获取

    image

    相关文章

      网友评论

        本文标题:Android MVP 运用反射配置泛型 Model

        本文链接:https://www.haomeiwen.com/subject/rmlejctx.html