Retrofit+Rxjava+Mvp实战

很久没写博客了,这段时间项目刚上线,闲来无事正好把项目里面遇到的一些问题整理一下,做个笔记。

如题,这次我要说的是我的项目架构:Retrofit+Rxjava+Mvp,提到这三个关键词想必大家都不陌生,Retrofit是当下比较流行的网络请求库,它采用注解的形式简化了用户对网络请求的参数、类型、Url等信息的操作,现在已经发展到Retrofit2---Retrofit2详解

Rxjava就更不用说了,是用来专门处理异步的,现在国内许多大牛都开始使用,用的人都说好,不墨迹资料戳这里-------Rxjava2学习资料站

Mvp是很久之前就出现的一种封装模式,我觉得最好的地方就是将视图逻辑和业务逻辑分开,降低了耦合,UI层只需调用,增加了代码的可读性,网上有各种实现MVP封装思路的版本,我用的应该算比较简洁的,这有一篇介绍的比较详细-------------MVP学习

先上几张截图:

api包下面处理的是项目里面所有的网络请求的接口

presenter包下处理的是网络请求的实现逻辑

view包下面处理的是网络请求完成之后的回调接口,这样UI层调用的时候可以做一下更新界面的操作

从上面的截图看来,如果你进行一个网络请求,你首先需要在api包下面注册接口,然后在view包下面注册回调,再在presenter包下面用rxjava实现网络请求的异步操作,将presenter和view进行绑定,最后在UI层调用presenter里面的方法触发回调view,在回调view中完成你后续的界面交互,这样才算走完整个流程。

这时候你可能会说我就写个网络请求为何要写这么多代码涉及到这么多类,你说的没错,起初我在使用这套东西的时候也很不适应,但当项目越来越大的时候我才发觉这样做极大的提高了代码的可读性,不至于你修改BUG的时候找个网络请求还要找半天,更不至于别人在读你代码的时候被你一个类里面的上千行代码秀的头皮发麻。

最后我会在文末给出我写的一个完整框架Demo的下载链接。

然后我再提一下我遇到的坑及一些好的点。有时候和后台调试接口的时候可能会遇到这样的问题,假如后台给你的一些字段类型是String默认值是null,这个时候前端在使用的时候为了防止空指针需要做非空判断。那么我在想是不是可以前端解析的时候将String的默认值设置为"",答案是可以的,由于我的Retrofit使用的解析器是Gson,你只需在创建Gson对象的时候这么做:

Gson gson = new GsonBuilder()
                //配置你的Gson
                .setDateFormat("yyyy-MM-dd hh:mm:ss")
                .registerTypeHierarchyAdapter(String.class,STRING)//设置解析的时候null转成""
                .create();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constant.API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                //添加rxjava2适配器
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
/**
 * 自定义TypeAdapter ,null对象将被解析成空字符串
 */
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
    public String read(JsonReader reader) {
        try {
            if (reader.peek() == JsonToken.NULL) {
                reader.nextNull();
                return ""; // 原先是返回null,这里改为返回空字符串
            }
            return reader.nextString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    public void write(JsonWriter writer, String value) {
        try {
            if (value == null) {
                writer.nullValue();
                return;
            }
            writer.value(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};

这是Gson自带的方法,其实Gson能做的远不只这些...

还有一点就是Retrofit默认是不能将接口请求的数据转成String,你需要单独配置一个转换器,如下:

public class StringConverter implements Converter<ResponseBody,String>{

    public static final StringConverter INSTANCE = new StringConverter();

    @Override
    public String convert(ResponseBody value) throws IOException {
        return value.string();
    }
}
public class StringConverterFactory extends Converter.Factory {

    public static final StringConverterFactory INSTANCE = new StringConverterFactory();

    public static StringConverterFactory create() {
        return INSTANCE;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        if (type == String.class) {
            return StringConverter.INSTANCE;
        }
        //其它类型我们不处理,返回null就行
        return null;
    }
}

由于我的Retrofit使用的网络框架是okhttp3所以只是将响应的ResponseBoy转换成String,最后在构建Retrofit的时候配置上:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constant.API_BASE_URL)
                //添加的fastjson解析器序列化数据
//                .addConverterFactory(new Retrofit2ConverterFactory())
                //添加转换器支持返回结果为String类型
                .addConverterFactory(StringConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                //添加rxjava2适配器
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

从上面也可以看出Retrofit也可以使用fastjson作为解析器。

最后我想说的一点是关于MVP内存泄漏的问题,之前有看网上说:

Presenter是同时持有View和Model的引用的,那么,当在Presenter中,假设由于业务需求,需要开辟一条线程进行耗时操作,如果此时View(Activity)退出,由于子线程还在运行,此时,Presenter不能被销毁(子线程是Presenter开辟的内部类,内部类隐式持有外部类的引用),由于Presenter持有view引用,故activity即使退出了,却不能被gc,此时,内存泄露便产生了。

那么我的解决方案是这样的:1、将view的强引用类型变为弱引用

protected T mView;
private WeakReference<T> weakView;

@Override
public void attachView(T view) {
    weakView = new WeakReference<>(view);
    mView = weakView.get();
}

@Override
public void detachView() {
    if (mView != null) {
        mView = null;
        weakView.clear();
        weakView = null;
    }
}

2、在activity和fragment销毁的时候detachView()及时释放资源

@Override
protected void onDestroy() {
    super.onDestroy();
    presenter.detachView();
}

3、在activity和fragment销毁的时候切断rxjava异步请求

 

这里最后还要提一点的就是,为了防止activity在某些情况下销毁的时候没走onDestroy,最好在你的presenter里面使用view的时候做个非空判断,因为我们的view已经变为弱引用被系统回收,而此时线程又没有结束(没走onDestroy而没有调用cancelAll()),如此应该就万无一失了。

效果图

GitHub下载地址

这次的分享就到这里了,如果您有什么好的建议或想法欢迎留言讨论。

2019-12-20更新,进阶Demo点这里:更好的解决了内存泄漏的问题-Demo

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值