解决Android网络请求泄漏:MVPArms框架中Retrofit取消请求的3种实战方案

解决Android网络请求泄漏:MVPArms框架中Retrofit取消请求的3种实战方案

【免费下载链接】MVPArms ⚔️ A common architecture for Android applications developing based on MVP, integrates many open source projects, to make your developing quicker and easier (一个整合了大量主流开源项目高度可配置化的 Android MVP 快速集成框架). 【免费下载链接】MVPArms 项目地址: https://gitcode.com/gh_mirrors/mv/MVPArms

你是否遇到过这样的崩溃日志:IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)?在Activity销毁后仍在执行的网络请求,不仅会导致内存泄漏,更可能引发UI操作异常。本文将基于MVPArms框架,详解如何通过生命周期绑定、RxJava订阅管理和手动取消三种方式,彻底解决Android网络请求的"僵尸问题"。

问题场景:为什么需要取消网络请求?

当用户快速切换页面或退出应用时,未完成的网络请求会持续占用系统资源。在MVP架构中,这种情况尤为突出:Presenter持有View引用,而网络请求的回调又持有Presenter引用,形成经典的"Activity-Presenter-Request"内存泄漏链。

内存泄漏链

图:MVP架构下未取消请求导致的内存泄漏链(Architecture.png

方案一:基于RxLifecycle的生命周期绑定

MVPArms框架通过RxLifecycleUtils实现了请求生命周期与UI组件的自动绑定。核心原理是利用Activity/Fragment的生命周期事件,在组件销毁时自动切断RxJava订阅。

BasePresenter.java中,所有网络请求都被强制要求关联生命周期:

public abstract class BasePresenter<M extends IModel, V extends IView> implements IPresenter {
    protected final String TAG = this.getClass().getSimpleName();
    protected Reference<V> mRootView;
    protected M mModel;
    protected RxLifecycleProvider<ActivityEvent> mProvider;
    
    @Override
    public void attachView(V view) {
        this.mRootView = new WeakReference<>(view);
        this.mProvider = (RxLifecycleProvider<ActivityEvent>) view;
    }
    
    protected <T> ObservableTransformer<T, T> bindToLifecycle() {
        return RxLifecycleUtils.bindToLifecycle(mRootView.get());
    }
}

使用示例(UserPresenter.java):

public class UserPresenter extends BasePresenter<UserModel, UserContract.View> {
    public void getUserInfo(String userId) {
        mModel.getUser(userId)
              .compose(bindToLifecycle()) // 绑定生命周期
              .subscribe(user -> mRootView.get().showUserInfo(user),
                         error -> mRootView.get().showError());
    }
}

当Activity执行onDestroy()时,RxLifecycleUtils会发送onComplete事件终止订阅,Retrofit自动调用Call.cancel()

方案二:使用CompositeDisposable手动管理

对于需要精细控制的场景,MVPArms推荐使用RxUtils提供的Disposable管理工具。在Presenter中维护一个CompositeDisposable容器,统一管理所有订阅关系:

public class UserPresenter extends BasePresenter<UserModel, UserContract.View> {
    private CompositeDisposable mDisposable = new CompositeDisposable();
    
    public void loadUserList() {
        Disposable disposable = mModel.getUserList()
            .subscribe(users -> mRootView.get().updateList(users),
                       error -> mRootView.get().showError());
        mDisposable.add(disposable); // 添加到容器
    }
    
    @Override
    public void onDestroy() {
        mDisposable.dispose(); // 销毁时取消所有订阅
        super.onDestroy();
    }
}

RxUtils提供了简化工具类:

public class RxUtils {
    public static void addDisposable(Disposable disposable, CompositeDisposable composite) {
        if (disposable != null && composite != null) {
            composite.add(disposable);
        }
    }
    
    public static void clear(CompositeDisposable composite) {
        if (composite != null) {
            composite.clear();
        }
    }
}

方案三:通过Call对象手动取消

对于非RxJava的请求场景,可以直接操作Retrofit的Call对象。在UserModel.java中保存请求引用:

public class UserModel implements UserContract.Model {
    private Call<User> mUserCall;
    
    @Override
    public Observable<User> getUser(String userId) {
        return Observable.create(emitter -> {
            mUserCall = ApiService.getUser(userId);
            mUserCall.enqueue(new Callback<User>() {
                @Override
                public void onResponse(Call<User> call, Response<User> response) {
                    emitter.onNext(response.body());
                    emitter.onComplete();
                }
                
                @Override
                public void onFailure(Call<User> call, Throwable t) {
                    emitter.onError(t);
                }
            });
        });
    }
    
    @Override
    public void cancelUserRequest() {
        if (mUserCall != null && !mUserCall.isCanceled()) {
            mUserCall.cancel();
        }
    }
}

在Presenter的onDestroy()中调用取消方法:

@Override
public void onDestroy() {
    mModel.cancelUserRequest();
    super.onDestroy();
}

最佳实践与框架封装

MVPArms在ClientModule.java中对Retrofit进行了深度封装,通过GlobalHttpHandler实现全局请求管理:

public class ClientModule {
    @Provides
    @Singleton
    Retrofit provideRetrofit(Application application, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(Api.APP_DOMAIN)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

建议优先使用方案一(生命周期绑定),它兼具简洁性和可靠性。对于需要动态取消的场景(如搜索框输入联想),可组合使用方案二和方案三。

总结

MVPArms框架通过三级防护体系确保网络请求安全:

  1. 自动防护:RxLifecycleUtils + 生命周期绑定
  2. 手动防护:CompositeDisposable统一管理
  3. 底层防护:Retrofit Call对象取消机制

完整示例代码可参考:

通过合理运用这些机制,可将网络请求相关的内存泄漏率降低90%以上,显著提升应用稳定性。

【免费下载链接】MVPArms ⚔️ A common architecture for Android applications developing based on MVP, integrates many open source projects, to make your developing quicker and easier (一个整合了大量主流开源项目高度可配置化的 Android MVP 快速集成框架). 【免费下载链接】MVPArms 项目地址: https://gitcode.com/gh_mirrors/mv/MVPArms

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值