2017-03-17 73 views
1

我在网上关注了几个教程和开源资源,并使用RxJava,Dagger 2和Retrofit构建了基于MVP体系结构的简单应用。一切工作正常,除非我开始下载数据,并立即旋转屏幕前一个请求被取消,并正在提出新的请求。Android MVP - 跨越配置更改而存活的网络请求

网络请求被取消的原因是我退出了我的视图的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和主持人之间的交互器:

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

} 

自定义范围,使演示者停留,只要查看活着。

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

匕首模块:

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

} 

匕首组件 - 子组件:

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

回答

-3

可以防止破坏活动(以及它的片段)与舱单一个简单的属性:

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

+0

从文档中的“处理配置改变自己可以使它更难以利用替代资源,因为系统不会自动应用它们。这种技术应被视为最后的手段,当你必须避免重新启动由于配置更改,不建议大多数应用程序。“ – Shmuel

0

您可以退订等待,直到您通过GetMovieListCallback收到MoviesInteractor的响应。

如果setViewonMovieFetchSuccessonMovieFetchFailed之前调用,那么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(); 
     } 
    } 
}