美文网首页Android开发网络
Android界面关闭网络请求未结束

Android界面关闭网络请求未结束

作者: Tyhj | 来源:发表于2019-05-15 16:15 被阅读320次

    原文链接:https://www.jianshu.com/p/1c856b6f1da2

    网络请求导致内存泄漏

    在执行网络请求的时候,网络不稳定或者超时的时候,获取数据时间比较长,用户可能已经退出这个界面了,这时候肯定会出现一些问题,首先因为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;
        }
    

    然后重写ActivityonDestroy()方法,调用Presenter.destroy()方法

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

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

    引入Lifecycles

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

    首先Lifecycles的话感觉还是比较简单的,从Activity拿到Lifecycle,然后就可以使用它来监听生命周期变化,我们只需要在Presenter里面监听到Destroy的时候就释放掉资源;activity需要继承至FragmentActivityAppCompatActivity也是一样的,它本身是继承至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比起来的话,只是取消了回调,没取消数据请求,问题不大

    相关文章

      网友评论

        本文标题:Android界面关闭网络请求未结束

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