Android界面關(guān)閉網(wǎng)絡(luò)請求未結(jié)束

原文鏈接:http://www.itdecent.cn/p/1c856b6f1da2

網(wǎng)絡(luò)請求導(dǎo)致內(nèi)存泄漏

在執(zhí)行網(wǎng)絡(luò)請求的時候,網(wǎng)絡(luò)不穩(wěn)定或者超時的時候,獲取數(shù)據(jù)時間比較長,用戶可能已經(jīng)退出這個界面了,這時候肯定會出現(xiàn)一些問題,首先因?yàn)?code>Presenter還在請求數(shù)據(jù),還持有Activity,就會導(dǎo)致內(nèi)存泄漏


    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()方法,里面關(guān)閉資源,釋放掉Activity

public void destory(){
        mActivity=null;
    }

然后重寫ActivityonDestroy()方法,調(diào)用Presenter.destroy()方法

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

這樣Activity關(guān)閉的時候的確不會出現(xiàn)內(nèi)存泄漏,但是獲取到數(shù)據(jù)后getView()方法會返回為空造成內(nèi)存泄漏,所以要判斷不為空;但是說實(shí)話感覺太麻煩了,這些簡單無腦的操作需要重復(fù)寫無數(shù)遍,肯定不能接受的

引入Lifecycles

Android Jetpack有一套可以解決的辦法,使用LiveDataLifecycles,但是我暫時感覺不是很適應(yīng),所以還是自己來實(shí)現(xiàn)一套。

首先Lifecycles的話感覺還是比較簡單的,從Activity拿到Lifecycle,然后就可以使用它來監(jiān)聽生命周期變化,我們只需要在Presenter里面監(jiān)聽到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的構(gòu)造方法里面都寫釋放資源的代碼也比較麻煩,可以寫一個BasePresener來統(tǒng)一監(jiān)聽生命周期變化釋放資源;而且通過反射其實(shí)可以釋放里面的所有資源

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 {
                        //釋放非靜態(tài)變量
                        Field[] fields = getClass().getDeclaredFields();
                        for (Field field : fields) {
                            try {
                                field.setAccessible(true);
                                //排除非靜態(tài)變量和基本類型
                                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;
    }

}

取消數(shù)據(jù)請求

上面的操作會自動釋放資源,但是還是需要在數(shù)據(jù)請求完成的時候判斷View是否為空,為了避免判空我們可以取消請求,或者不執(zhí)行數(shù)據(jù)返回回調(diào);RxLifecycle 該項(xiàng)目是為了防止RxJava 中subscription導(dǎo)致內(nèi)存泄漏而誕生的,核心思想是通過監(jiān)聽 Activity、Fragment 的生命周期,來自動斷開 subscription 以防止內(nèi)存泄漏。

基本用法如下:

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

其實(shí)和上面的道理一樣,當(dāng)監(jiān)聽到Destroy的時候中斷數(shù)據(jù)請求,那么IDataCallback就不會執(zhí)行回調(diào),自然不用去管getView()是否會為空;

方法其實(shí)還可以,只是我每次調(diào)用Model層的時候都需要把當(dāng)前頁面的lifecycle傳進(jìn)去,這個參數(shù)其實(shí)對于Model層來說是沒有意義的,但是我的接口卻必須加上這個參數(shù),感覺還是有些不好;

IDataCallback執(zhí)行回調(diào),那應(yīng)該可以從它入手

public interface IDataCallback<T> {
    /**
     * 獲取數(shù)據(jù)成功
     *
     * @param t
     */
    void success(T t);


    /**
     * 獲取數(shù)據(jù)失敗
     *
     * @param msg
     */
    void fail(String msg);
}

這是自己定義的返回數(shù)據(jù)的接口,很簡單,每次獲取到數(shù)據(jù)后調(diào)用回調(diào)方法

    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);
                    }
                });

我們想在監(jiān)聽到頁面關(guān)閉的時候不執(zhí)行回調(diào),就需要實(shí)現(xiàn)其中的方法,改動一下;

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

    /**
     * 是否中斷回調(diào)
     */
    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);
        }
    }

    /**
     * 數(shù)據(jù)返回
     *
     * @param t
     */
    protected abstract void dataBack(T t);

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

}

也非常簡單,構(gòu)造方法可以傳入Lifecycle,監(jiān)聽到生命周期結(jié)束,那么不執(zhí)行新的數(shù)據(jù)返回的回調(diào)方法,請求數(shù)據(jù)在設(shè)置監(jiān)聽的時候,重寫這兩個新的抽象方法就好了

 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);
            }
        });
    }

感覺也還可以吧,沒有依賴其他的框架,實(shí)現(xiàn)起來很簡單,的確也解決了問題,用起來也方便;和RxLifecycle比起來的話,只是取消了回調(diào),沒取消數(shù)據(jù)請求,問題不大

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容