Presenter recovery for ViewPager snippets (MVP)

I am trying to reorganize an existing application to use the MVP architecture. One activity has a ViewPager with three fragments. Each fragment is associated with a leader. To be precise - each presenter is given a View to work with, that is, a Fragment . At the moment, I am creating these presenters inside the ViewPager adapter, especially in the getItem(int position) method.

 Fragment fragment = FirstFragment.newInstance(); FirstPresenter presenter = new FirstPresenter(repo, (FirstContract.View) fragment, projectId, userId); 

The problem that I encountered is that the process is killed and then restarted, the ViewPager has its own life cycle, and therefore getItem not called again - fragments are automatically recreated without invitations.

Is there a solution to this problem?

+6
source share
4 answers

Since there is still no perfect answer to this question, I thought it would be nice to share my interim solution.

As I mentioned in one of the comments, the goal here is to restore the ViewPager from the kill process and, ideally, preserve the Presenter initialization extracted from the view. At the moment, my solution is to override restoreState(Parcelable state, ClassLoader loader) inside the FragmentStatePagerAdapter , check the Parcelable state, similar to the actual implementation of the restoreState method, and then for each fragment of a particular class, I can initialize the presenter and assign it a view.

 @Override public void restoreState(Parcelable state, ClassLoader loader) { if (state != null) { Bundle bundle = (Bundle)state; bundle.setClassLoader(loader); Iterable<String> keys = bundle.keySet(); for (String key: keys) { if (key.startsWith("f")) { Fragment f = mFragmentManager.getFragment(bundle, key); if (f != null) { if (f instanceof FirstFragment) { new FirstPresenter(repo, (FirstContract.View) f, projectId, userId); } } else { Log.w(TAG, ".restoreState() - bad fragment at key " + key); } } } } super.restoreState(state, loader); } 
+1
source

As stated in the comments, the Presenter must be attached (and detached) to the Activity / Fragment lifecycle methods. Not in external classes, because only View can connect-disconnect the Host at the appropriate time. But it’s good practice to initialize Presenter in a separate class (or dependency injection infrastructure) to separate it from View .

0
source

The recommended answer did not work for me, since mFragmentManager is a private member of the FragmentStatePagerAdapter . I do not know how this works for vkislicins. Instead, I just called, getting the parent class to do restoreState , then grabbed the fragments using "instantiateItem". For instance:

 @Override public void restoreState(Parcelable state, ClassLoader loader) { // this will load all the fragments again super.restoreState(state, loader); // since the fragments are now loaded, instantiate can be used because it just returns them MyFragmentClass tab1 = (MyFragmentClass) instantiateItem(null, 0); tab1Presenter.setView(tab1); tab1.setPresenter(tab1Presenter); // then just do the same for the other fragments ... } 

Feels a bit hacky, but it works.

0
source

Firstly, my solution includes the FragmentManager.FragmentLifecycleCallbacks , which is

Callback interface for listening to fragment state changes that occur in the given FragmentManager

and adheres to separation of concerns, as shown in Android Architecture Blueprints , I would say.

  • Activity creates a Presenter by going through View / Fragment , so that
  • Presenter knows its View and, in addition, sets its Presenter

In Activity onCreate I register the FragmentLifecycleCallbacks listener, calling this

 private void registerFragmentsLifecycleListener() { // All registered callbacks will be automatically unregistered when // this FragmentManager is destroyed. getSupportFragmentManager.registerFragmentLifecycleCallbacks( new FragmentManager.FragmentLifecycleCallbacks() { // Called after the fragment has returned from its onActivityCreated @Override public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { createPresenter(f); } }, false); // true to register callback for all child FragmentManagers } 

The listener receives a notification after the Fragment returns from its onActivityCreated , to ensure that only for each new Fragment instance added by the ViewPager new Presenter will be created. A fragment can be attached / detached, its view can be created / destroyed a couple of times, nothing had to be done, I still got Presenter .

Because in case of rest (for example, by rotation) Fragment s' onCreate is called before Activity one (where the FragmentLifecycleCallbacks listener is registered!), The listener could not implement onFragmentCreated , it must be onFragmentActivityCreated .

For this new Fragment instance, we can determine which Presenter needed:

 private void createPresenter(Fragment fragment) { if (fragment instanceof WhateverContract.View) { WhateverContract.Presenter whateverPresenter = new WhateverPresenter((WhateverContract.View) fragment); } else if (...){} } 

Presenter connects to its View / Fragment in the constructor

 private final WhateverContract.View mView; public WhateverPresenter(@NonNull WhateverContract.View view) { mView = checkNotNull(view, "view cannot be null!"); mView.setPresenter(this); } 

and then can be run in Fragment onResume .


If something is wrong or improve, let me know :)

0
source

Source: https://habr.com/ru/post/1014963/


All Articles