Search code examples
javaandroiddagger-2

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?


Solution

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