[Android]网络请求中关闭页面造成内存泄漏的解决办法

网络请求导致内存泄漏

在执行网络请求的时候,网络不稳定或者超时的时候,获取数据时间比较长,用户可能已经退出这个界面了,这时候肯定会出现一些问题,首先因为Presenter还在请求数据,还持有Activity,就会导致内存泄漏。

    public NotPassPresenter(NotPassActivity activity) {
        mActivity = activity;
        mRepository = new ExpressRepository();
    }

    @Override
    public void getNotPassRecord() {
        mRepository.getNotPassRecord(new IDataCallback<List<NotPassRecord>>() {
            @Override
            public void success(List<NotPassRecord> notPassRecords) {
                getView().showNotPassRecord(notPassRecords);
            }

            @Override
            public void fail(String msg) {
                getView().showMsg(msg);
            }
        });
    }
    
    @Override
    public NotPassContract.View getView() {
        return mActivity;
    }

简单的解决方案

解决方案很简单,在Presenter中写一个destroy()方法,里面关闭资源,释放掉Activity。

public void destory(){
        mActivity=null;
    }

然后重写Activity的onDestroy()方法,调用Presenter.destroy()方法。

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mNotPassPresenter.destory();
    }

这样Activity关闭的时候的确不会出现内存泄漏,但是获取到数据后getView()方法会返回为空造成内存泄漏,所以要判断不为空;但是说实话感觉太麻烦了,这些简单无脑的操作需要重复写无数遍,肯定不能接受的。

引入Lifecycles

Android Jetpack有一套可以解决的办法,使用LiveData和Lifecycles,但是我暂时感觉不是很适应,所以还是自己来实现一套。

首先Lifecycles的话感觉还是比较简单的,从Activity拿到Lifecycle,然后就可以使用它来监听生命周期变化,我们只需要在Presenter里面监听到Destroy的时候就释放掉资源;activity需要继承至FragmentActivity,AppCompatActivity也是一样的,它本身是继承至FragmentActivity的。

    public NotPassPresenter(NotPassActivity activity) {
        mActivity = activity;
        mRepository = new ExpressRepository();
        activity.getLifecycle().addObserver(new GenericLifecycleObserver() {
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    mActivity = null;
                    mRepository = null;
                }
            }
        });
    }

如果在每个Presenter的构造方法里面都写释放资源的代码也比较麻烦,可以写一个BasePresener来统一监听生命周期变化释放资源;而且通过反射其实可以释放里面的所有资源。

package com.dhht.baselibrary.app;

import android.arch.lifecycle.GenericLifecycleObserver;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;

/**
 * @author HanPei
 * @date 2019/5/15  下午4:39
 */
public abstract class BasePresenter<T> {

    T mView;

    Lifecycle mLifecycle;

    protected BasePresenter(T view) {
        mView = view;
        FragmentActivity activity = null;
        if (mView instanceof FragmentActivity) {
            activity = (FragmentActivity) mView;
        } else if (mView instanceof Fragment) {
            activity = ((Fragment) mView).getActivity();
        }
        if (activity != null) {
            mLifecycle = activity.getLifecycle();
            mLifecycle.addObserver(new GenericLifecycleObserver() {
                @Override
                public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_DESTROY) {
                        release();
                    }
                }
            });
        }
    }

    /**
     * 释放资源
     */
    private void release() {
        mView = null;
        mLifecycle = null;

/*        Observable.just(0)
                .observeOn(Schedulers.newThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        //释放非静态变量
                        Field[] fields = getClass().getDeclaredFields();
                        for (Field field : fields) {
                            try {
                                field.setAccessible(true);
                                //排除非静态变量和基本类型
                                if (!field.getType().isPrimitive() && !Modifier.isStatic(field.getModifiers())) {
                                    field.set(this, null);
                                }
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });*/
    }

    /**
     * 获取View
     *
     * @return
     */
    protected T getView() {
        return mView;
    }

    protected Lifecycle getLifecycle() {
        return mLifecycle;
    }

}

取消数据请求

上面的操作会自动释放资源,但是还是需要在数据请求完成的时候判断View是否为空,为了避免判空我们可以取消请求,或者不执行数据返回回调;RxLifecycle 该项目是为了防止RxJava 中subscription导致内存泄漏而诞生的,核心思想是通过监听 Activity、Fragment 的生命周期,来自动断开 subscription 以防止内存泄漏。

基本用法如下:

myObservable
    .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
    .subscribe();

其实和上面的道理一样,当监听到Destroy的时候中断数据请求,那么IDataCallback就不会执行回调,自然不用去管getView()是否会为空。

方法其实还可以,只是我每次调用Model层的时候都需要把当前页面的lifecycle传进去,这个参数其实对于Model层来说是没有意义的,但是我的接口却必须加上这个参数,感觉还是有些不好。

IDataCallback执行回调,那应该可以从它入手。

public interface IDataCallback<T> {
    /**
     * 获取数据成功
     *
     * @param t
     */
    void success(T t);


    /**
     * 获取数据失败
     *
     * @param msg
     */
    void fail(String msg);
}

这是自己定义的返回数据的接口,很简单,每次获取到数据后调用回调方法。

    mRetrofitApi.getNotPassRecord()
                .compose(Retrofite.applySchedulers())
                .subscribe(new BaseObserver<List<NotPassRecord>>() {
                    @Override
                    public void sucess(List<NotPassRecord> notPassRecords) {
                        callback.success(notPassRecords);
                    }

                    @Override
                    public void erro(String msg) {
                        callback.fail(msg);
                    }
                });

我们想在监听到页面关闭的时候不执行回调,就需要实现其中的方法,改动一下。

public abstract class BaseDataCallBack<T> implements IDataCallback<T> {

    /**
     * 是否中断回调
     */
    private boolean breakOff;

    public BaseDataCallBack() {

    }

    public BaseDataCallBack(Lifecycle lifecycle) {
        lifecycle.addObserver(new GenericLifecycleObserver() {
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (event.equals(Lifecycle.Event.ON_DESTROY)) {
                    breakOff = true;
                }
            }
        });
    }

    @Override
    public void success(T t) {
        if (!breakOff) {
            dataBack(t);
        }
    }

    @Override
    public void fail(String msg) {
        if (!breakOff) {
            erroBack(msg);
        }
    }

    /**
     * 数据返回
     *
     * @param t
     */
    protected abstract void dataBack(T t);

    /**
     * 返回出错信息
     *
     * @param msg
     */
    protected abstract void erroBack(String msg);

}

也非常简单,构造方法可以传入Lifecycle,监听到生命周期结束,那么不执行新的数据返回的回调方法,请求数据在设置监听的时候,重写这两个新的抽象方法就好了。

 public void getNotPassRecord() {
        mRepository.getNotPassRecord(new BaseDataCallBack<List<NotPassRecord>>() {

            @Override
            protected void dataBack(List<NotPassRecord> notPassRecords) {
                getView().showNotPassRecord(notPassRecords);
            }

            @Override
            protected void erroBack(String msg) {
                getView().showMsg(msg);
            }
        });
    }

感觉也还可以吧,没有依赖其他的框架,实现起来很简单,的确也解决了问题,用起来也方便;和RxLifecycle比起来的话,只是取消了回调,没取消数据请求,问题不大。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值