2

I'm building an Android application using MVP. One of the screen displays data in a sort of Master/Detail view (a content page embedding a player):

mockup

At the top, there is the player (fragment) and below is the content info (viewpager with fragments), and user can swipe between pages (ViewPager) to switch between contents. When the Viewpager position changes, the player is updated accordingly and starts playing the current content.

Data requested by the presenter comes from a repository, with local(database)/remote(Rest API) datasources.

How can I make the best use of MVP to avoid requesting the same data multiple times?

Should I have only one Presenter in my Activity, and persist/cache data requested for the Content Info Info somewhere, to pass it back to the player when the position is changed?

Or is it better to have a presenter for each ContentInfo, requesting its own data, and a presenter for the player, requesting the same data again when the play() method is called?


I found this related topic, but it doesn't really fit to my case, as the data is not updated in the player at the same time that it is requested in the Content Info fragment (I'm fetching the data onCreate of the fragment by the ViewPager to have it already loaded when the swipe is done).

w00ly
  • 455
  • 1
  • 5
  • 18

2 Answers2

1

The best approach is to make individual components request the data from the repository, and handle the caching at repository level.

When the contents of ViewPager changes, just notify the player about the ID of a new content. The player will then fetch this content from the repository. The repository "knows" that this content has already been downloaded and serves it from the database instead of web.

If the database is not fast enough, it is possible to implement an in-memory caching inside the repository.

Vasiliy
  • 16,221
  • 11
  • 71
  • 127
1

You can use Fragment with setRetainInstance(true) as the data fragment. setRetainInstance controls whether a fragment instance is retained across Activity re-creation (such as from a configuration change).

An example of data fragment here:

public class MyDataFragment extends Fragment {
    public static MyDataFragment newInstance() {
        return new MyDataFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Retain this instance so it isn't destroyed when MainActivity and MainFragment change configuration.
        setRetainInstance(true);
    }    

    // Add any data you want to share across multiple fragments of same activity here
    public String myData = null;
}

Let say Activity class is named MyActivity:

public class MyActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ....

        // Build up a common data fragment to be used by multiple child view pager fragments.
        final FragmentManager fm = getSupportFragmentManager();
        MyDataFragment myDataFragment = (MyDataFragment)fm.findFragmentByTag(MY_DATA_FRAGMENT);

        if (myDataFragment == null) {
            MyDataFragment dataFragment = MyDataFragment.newInstance();
            fm.beginTransaction().add(dataFragment, MY_DATA_FRAGMENT).commit();
        }

        .....
    }

    ....

    public static final String MY_DATA_FRAGMENT = "MY_DATA_FRAGMENT";

Then in your player fragment or viewpager fragment, access the data fragment like this:

@Override
public void onCreate (Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // access data fragment
    final FragmentManager fm = getFragmentManager();
    this.myDataFragment = (MyDataFragment)fm.findFragmentByTag(MyActivity.MY_DATA_FRAGMENT);

    .....
}

@Override
public void onResume() {
    super.onResume();

    if (myDataFragment.myData != null) {
        // data already exists, SKIP to make network call. Process it directly, eg: update UI
        .....
    } else {
        // fetch data from network via asynchronously task ---- [1]
        // assume upon finish, the asynchronously task will invoke "onDataLoadFinish()" method below:
        ....
    }
}

// assume this will be invoked by asynchronously network task [1] above
public void onDataLoadFinish (String data) {
    // set data to data fragment, which can be reused by multiple fragments
    this.myDataFragment.myData = data;
}

.....

private MyDataFragment myDataFragment = null;

Hope this help, good luck!

Shuwn Yuan Tee
  • 5,578
  • 6
  • 28
  • 42