TomaszKopacz
TomaszKopacz

Reputation: 93

How to properly inject FragmentPagerAdapter into my activity class with Dagger2?

I am trying to make a module class AdaptersModules, which provides ViewPagerAdapter. ViewPagerAdapter is a class extending FragmentPagerAdapter. ViewPagerAdapter constructor requires FragmentManager.

My activity class is:

public class TabsActivity extends AppCompatActivity {

public FragmentManager fm = getSupportFragmentManager();

@Inject ViewPagerAdapter adapter;
@Inject HomeFragment homeFragment;
@Inject ChallengeFragment challengeFragment;
@Inject FriendsFragment friendsFragment;
@Inject OptionsFragment optionsFragment;
@Inject QuickPhotoFragment quickPhotoFragment;
@Inject TrophiesFragment trophiesFragment;


@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.tabsTabLayout) TabLayout tabLayout;
@BindView(R.id.tabsViewPager) ViewPager viewPager;

private int[] tabIcons = {  R.drawable.ic_home,
                            R.drawable.ic_quick_photo,
                            R.drawable.ic_challenge,
                            R.drawable.ic_friends,
                            R.drawable.ic_trophies,
                            R.drawable.ic_settings  };

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_tabs);
    ButterKnife.bind(this);
    ((MeetBamApplication)getApplication())
            .getComponent()
            .inject(this);


    setSupportActionBar(toolbar);
    getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(true);

    setupViewPager(viewPager);

    tabLayout.setupWithViewPager(viewPager);
    for (int i=0; i<viewPager.getAdapter().getCount(); i++)
        tabLayout.getTabAt(i).setIcon(tabIcons[i]);
}

private void setupViewPager(ViewPager viewPager) {

    adapter.addFragment(homeFragment);
    adapter.addFragment(quickPhotoFragment);
    adapter.addFragment(challengeFragment);
    adapter.addFragment(friendsFragment);
    adapter.addFragment(trophiesFragment);
    adapter.addFragment(optionsFragment);
    viewPager.setAdapter(adapter);
}
}

My module class:

 @Module
 public class AdaptersModules extends TabsActivity{

@Singleton
@Provides
protected ViewPagerAdapter provideViewPagerAdapter(){
    return new ViewPagerAdapter(fm);
}
}

And component:

 @Singleton
 @Component(modules = {
         AppModule.class,
         TabsModules.class,
         AdaptersModules.class})
 public interface ApplicationComponent {

    void inject(MeetBamApplication meetBamApplication);
    void inject(TabsActivity activity);
}

ViewPagerAdapter class is:

public class ViewPagerAdapter extends FragmentPagerAdapter {

     private final List<Fragment> mFragmentList = new ArrayList<>();

     public void addFragment(Fragment fragment){ mFragmentList.add(fragment);}

     public ViewPagerAdapter(FragmentManager fm) { super(fm); }

     @Override
     public Fragment getItem(int position) { return mFragmentList.get(position); }

     @Override
     public int getCount() { return mFragmentList.size(); }
 }

When I run and test app I get:

 FATAL EXCEPTION: main
                                                                          Process: com.thomsoncompany.meetbamgo, PID: 23733
                                                                          java.lang.IllegalStateException: Activity has been destroyed
                                                                              at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1515)
                                                                              at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:638)
                                                                              at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:621)
                                                                              at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:139)
                                                                              at android.support.v4.view.ViewPager.populate(ViewPager.java:1177)
                                                                              at android.support.v4.view.ViewPager.populate(ViewPager.java:1025)
                                                                              at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1545)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5625)
                                                                              at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:668)
                                                                              at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:735)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5625)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:459)
                                                                              at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5625)
                                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1692)
                                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:760)
                                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:629)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5625)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:459)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5625)
                                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1692)
                                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:760)
                                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:629)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5625)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:459)
                                                                              at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2644)
                                                                              at android.view.View.measure(View.java:17782)
                                                                              at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2389)
                                                                              at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1399)
                                                                              at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1623)
                                                                              at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1277)
                                                                              at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6483)
                                                                              at android.view.Choreographer$CallbackRecord.run(Choreographer.java:802)
                                                                              at android.view.Choreographer.doCallbacks(Choreographer.java:605)
                                                                              at android.view.Choreographer.doFrame(Choreographer.java:574)
                                                                              at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:788)
                                                                              at android.os.Handler.handleCallback(Handler.java:815)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:104)
                                                                              at android.os.Looper.loop(Looper.java:194)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5549)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                              at java.lang.reflect.Method.invoke(Method.java:372)
                                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)

What should I do?

Upvotes: 5

Views: 2816

Answers (1)

David Rawson
David Rawson

Reputation: 21447

When you are using Dagger 2 in Android you need to be very careful to make sure that your injected members track the lifecycles of the injection targets correctly.

ViewPagerAdapter depends on a FragmentManager. FragmentManager is not an application singleton. A single FragmentManager will become available to an Activity some time after onCreate() and will follow the lifecycle of that Activity only. In other words, when that Activity is destroyed then the FragmentManager is unavailable for use. If you retain a reference to it afterwards by making it a singleton then you will get a memory leak and the above error because the Activity with which it is associated will no longer exist.

The correct way to handle this is to make a new component that tracks the lifecycle of your Activity:

@PerActivity
@Component( dependencies =  { ApplicationComponent.class }, modules = { TabsModule.class, AdaptersModule.class, TabActivityModule.class }
public interface TabComponent {
    void inject(TabsActivtiy activity);
}

Then create a module for your Activity that can provide the members that need to track the lifecycle of that Activity:

@Module
public class TabActivityModule {

    private final TabActivity activity;

    public TabActivityModule(TabActivity activity) {
        this.activity = activity;
    }

    @Provides
    @PerActivity
    FragmentManager fragmentManager() {
        return activity.getFragmentManager(); 
    }
}

Upvotes: 4

Related Questions