Android MVP - 在配置更改后幸存的网络请求

时间:2017-03-17 01:04:37

标签: android mvp dagger-2 rx-android onconfigurationchanged

我在Web上关注了几个教程和开源,并使用RxJava,Dagger 2和Retrofit构建了基于MVP架构的简单应用程序。一切正常,除非我开始下载数据并立即旋转屏幕,之前的请求被取消并且正在发出新的请求。

网络请求被取消的原因是我取消订阅了我的View onDestroyView内的Observable。那是为了防止内存泄漏!

如何保留先前的网络请求,不让Subscription泄露?

此处查看:

public class MoviesFragment extends Fragment implements MoviesView{

    @Inject
    MoviesPresenter moviesPresenter;
   //....

    public MoviesFragment(){

    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        setRetainInstance(true);
        ((BaseApplication) getActivity().getApplication()).createListingComponent().inject(this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.fragment_movies, container, false);
        ButterKnife.bind(this, rootView);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        moviesPresenter.setView(this);
    }

    // ....

    @Override
    public void onDestroyView()
    {
        super.onDestroyView();
        moviesPresenter.destroy();
        ButterKnife.unbind(this);
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        ((BaseApplication)getActivity().getApplication()).releaseListingComponent();
    }
    //....
}

此处的演讲者:

public class MoviesPresenterImpl implements MoviesPresenter {

    private final MoviesInteractor moviesInteractor;
    private MoviesView view;
    private Subscription fetchSubscription;

    public MoviesPresenterImpl(MoviesInteractor moviesInteractor) {
        this.moviesInteractor = moviesInteractor;
    }

    @Override
    public void downloadMovies() {

        fetchSubscription = moviesInteractor.getMovieList(new MoviesInteractorImpl.GetMovieListCallback() {
            @Override
            public void onSuccess(List<MovieModel> movieModels) {
                onMovieFetchSuccess(movieModels);
            }

            @Override
            public void onError(NetworkError networkError) {
                onMovieFetchFailed(new Throwable(networkError));
            }
        });

    }

    @Override
    public void setView(MoviesView view) {
        this.view = view;
        downloadMovies();
    }

    @Override
    public void destroy() {
        view = null;
        fetchSubscription.unsubscribe();
    }

    private void onMovieFetchSuccess(List<MovieModel> movies) {
        if (isViewAttached()) {
            view.showMovies(movies);
        }
    }

    //....
}

这是API和Presenter之间的交互者:

public class MoviesInteractorImpl implements MoviesInteractor {

    private Observable<MoviesResponseModel> call;

    public MoviesInteractorImpl(MoviesRetrofitService moviesRetrofitService) {
        call = moviesRetrofitService.getMovies("en", "popularity.desc", "MY_API_KEY");
    }

    @Override
    public Subscription getMovieList(final GetMovieListCallback callback) {

        return call
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<MoviesResponseModel>() {
                    @Override
                    public void onStart() {
                        super.onStart();
                    }

                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        callback.onError(new NetworkError(e));
                    }

                    @Override
                    public void onNext(MoviesResponseModel cityListResponse) {
                        callback.onSuccess(cityListResponse.getMovieList());
                    }
                });
    }

    public interface GetMovieListCallback {
        void onSuccess(List<MovieModel> movieModels);

        void onError(NetworkError networkError);
    }

}

自定义范围,以便只要View处于活动状态,Presenter就会保留。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ListingScope {
}

Dagger模块:

@Module
public class ListingModule {

    @Provides
    public MoviesRetrofitService provideMoviesRetorfitService(Retrofit retrofit) {
        return retrofit.create(MoviesRetrofitService.class);
    }

    @Provides
    MoviesInteractor provideMoviesInteractor(MoviesRetrofitService moviesRetrofitService){
        return new MoviesInteractorImpl(moviesRetrofitService);
    }

    @Provides
    MoviesPresenter provideMoviesPresenter(MoviesInteractor moviesInteractor){
        return new MoviesPresenterImpl(moviesInteractor);
    }

}

Dagger Component - 子组件:

@ListingScope
@Subcomponent(modules = {ListingModule.class})
public interface ListingComponent {
    MoviesFragment inject(MoviesFragment moviesFragment);
}

2 个答案:

答案 0 :(得分:0)

您可以等待取消订阅,直到您通过GetMovieListCallback从MoviesInteractor收到回复。

如果在setViewonMovieFetchSuccess之前调用onMovieFetchFailed,则fetchSubscription将不会取消订阅。 如果在重置MoviesView之前完成提取,您可以在MoviesView重置后立即保存该电影并更新视图。

public class MoviesInteractorImpl implements MoviesInteractor {
    private boolean needToUnsubscribe = false;
    private List<MovieModel> lastMovies;

    //....

    @Override
    public void setView(MoviesView view) {
        this.view = view;
        needToUnsubscribe = false;
        if(lastMovies != null) {
            view.showMovies(movies);
        }
        downloadMovies();
    }

    @Override
    public void destroy() {
        view = null;
        needToUnsubscribe = true;
    }

    private void onMovieFetchSuccess(List<MovieModel> movies) {
        lastMovies = movies;
        if (isViewAttached()) {
            view.showMovies(movies);
        }
        if(needToUnsubscribe) {
            fetchSubscription.unsubscribe();
        }
    }

    private void onMovieFetchFailed(Throwable throwable) {
         //....
        lastMovies = null;
        if(needToUnsubscribe) {
            fetchSubscription.unsubscribe();
        }
    }
}

答案 1 :(得分:-3)

你可以使用manifest上的一个简单属性来防止破坏活动(以及它的片段):

android:configChanges="orientation|screenSize"

使用此选项,当您旋转屏幕时,活动将不会执行任何生命周期方法(onResume,onPause等)

有一个具体的问题: Android, how to not destroy the activity when I rotate the device?

官方文件:https://developer.android.com/guide/topics/resources/runtime-changes.html