我在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);
}
答案 0 :(得分:0)
您可以等待取消订阅,直到您通过GetMovieListCallback从MoviesInteractor收到回复。
如果在setView
或onMovieFetchSuccess
之前调用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