Skip to content

Instantly share code, notes, and snippets.

@vestrel00
Last active October 7, 2021 19:59
Show Gist options
  • Save vestrel00/64be913f954989fe52c674247e093218 to your computer and use it in GitHub Desktop.
Save vestrel00/64be913f954989fe52c674247e093218 to your computer and use it in GitHub Desktop.
A: Dagger.android 2.11 simple example with support for Singleton, PerActivity, PerFragment, and PerChildFragment scopes
// This is a super simplified example of how to use the new dagger.android framework
// introduced in Dagger 2.10. For a more complete, in-depth guide to dagger.android
// read https://proandroiddev.com/how-to-android-dagger-2-10-2-11-butterknife-mvp-part-1-eb0f6b970fd
// For a complete codebase using dagger.android 2.11-2.17, butterknife 8.7-8.8, and MVP,
// see https://github.com/vestrel00/android-dagger-butterknife-mvp
// This example works with Dagger 2.11-2.17. Starting with Dagger 2.11,
// @ContributesAndroidInjector was introduced removing the need to define @Subcomponent classes.
// App.java
// Could also extend DaggerApplication instead of implementing HasActivityInjector
public class App extends Application implements HasActivityInjector {
@Inject
AppDependency appDependency;
@Inject
DispatchingAndroidInjector<Activity> activityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create().inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return activityInjector;
}
}
// AppModule.java
@Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
@PerActivity
@ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity mainActivityInjector();
}
// AppComponent.java
@Singleton
@Component(modules = AppModule.class)
interface AppComponent {
void inject(App app);
}
// MainActivity.java
// Could also extend DaggerActivity instead of implementing HasFragmentInjector
public final class MainActivity extends Activity implements HasFragmentInjector {
@Inject
AppDependency appDependency; // same object from App
@Inject
ActivityDependency activityDependency;
@Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
addFragment(R.id.fragment_container, new MainFragment());
}
}
@Override
public final AndroidInjector<Fragment> fragmentInjector() {
return fragmentInjector;
}
private final void addFragment(@IdRes int containerViewId, Fragment fragment) {
getFragmentManager.beginTransaction()
.add(containerViewId, fragment)
.commit();
}
}
// MainActivityModule.java
@Module
public abstract class MainActivityModule {
@PerFragment
@ContributesAndroidInjector(modules = MainFragmentModule.class)
abstract MainFragment mainFragmentInjector();
}
// MainFragment.java
// Could also extend DaggerFragment instead of implementing HasFragmentInjector
// Could instead extend DialogFragment to add DialogFragment capabilities.
// DialogFragments may be embedded as regular fragments in a view of an Activity or Fragment
// and may also be shown as a dialog or in an alert dialog.
public final class MainFragment extends Fragment implements HasFragmentInjector {
@Inject
AppDependency appDependency; // same object from App
@Inject
ActivityDependency activityDependency; // same object from MainActivity
@Inject
FragmentDependency fragmentDependency;
@Inject
DispatchingAndroidInjector<Fragment> childFragmentInjector;
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Perform injection here for versions before M as onAttach(*Context*) did not yet exist
// This fixes DaggerFragment issue: https://github.com/google/dagger/issues/777
AndroidInjection.inject(this);
}
super.onAttach(activity);
}
@Override
public void onAttach(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Perform injection here for M (API 23) due to deprecation of onAttach(*Activity*).
AndroidInjection.inject(this);
}
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_fragment, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
addChildFragment(R.id.child_fragment_container, new MainChildFragment());
}
}
@Override
public final AndroidInjector<Fragment> fragmentInjector() {
return childFragmentInjector;
}
private final void addChildFragment(@IdRes int viewId, Fragment fragment) {
getChildFragmentManager()
.beginTransaction()
.add(viewId, fragment)
.commit();
}
}
// MainFragmentModule.java
@Module
public abstract class MainFragmentModule {
@PerChildFragment
@ContributesAndroidInjector(modules = MainChildFragmentModule.class)
abstract MainChildFragment mainChildFragmentInjector();
}
// MainChildFragment.java
public final class MainChildFragment extends Fragment {
@Inject
AppDependency appDependency; // same object from App
@Inject
ActivityDependency activityDependency; // same object from MainActivity
@Inject
FragmentDependency fragmentDependency; // same object from MainFragment
@Inject
ChildFragmentDependency childFragmentDependency;
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Perform injection here for versions before M as onAttach(*Context*) did not yet exist
// This fixes DaggerFragment issue: https://github.com/google/dagger/issues/777
AndroidInjection.inject(this);
}
super.onAttach(activity);
}
@Override
public void onAttach(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Perform injection here for M (API 23) due to deprecation of onAttach(*Activity*).
AndroidInjection.inject(this);
}
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_child_fragment, container, false);
}
}
// MainChildFragmentModule.java
@Module
public abstract class MainChildFragmentModule {
}
// PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}
// PerFragment.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerFragment {
}
// PerChildFragment.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerChildFragment {
}
// AppDependency.java
@Singleton
public final class AppDependency {
@Inject
AppDependency() {
}
}
// ActivityDependency.java
@PerActivity
public final class ActivityDependency {
@Inject
ActivityDependency() {
}
}
// FragmentDependency.java
@PerFragment
public final class FragmentDependency {
@Inject
FragmentDependency() {
}
}
// ChildFragmentDependency.java
@PerChildFragment
public final class ChildFragmentDependency {
@Inject
ChildFragmentDependency() {
}
}
@alouanemed
Copy link

Thank you for the example.

I tried to implement the same approach in my project but am having an issue. : google/dagger#899

Can you check it out ?

@vestrel00
Copy link
Author

Here is my response to that: google/dagger#899 (comment)

@ezamelczyk
Copy link

This isn't working. I copied this gist letter for letter and I'm getting error No injector factory bound for Class MainChildFragment

@vestrel00
Copy link
Author

vestrel00 commented Apr 27, 2018

Hey there @ezamelczyk! I just saw your comment about the error you are getting. I was able to reproduce the error. The error is caused by code that is not shown in this gist, which is MainFragment#addChildFragment(int viewId, Fragment fragment).

You need to make sure that you use the childFragmentManager like so;

class MainFragment {
    void addChildFragment(int viewId, Fragment fragment) {
        getChildFragmentManager()
                .beginTransaction()
                .add(viewId, fragment)
                .commit();
    }
}

The error you are getting is due to the use of the regular fragment manager;

class MainFragment {
    void addChildFragment(int viewId, Fragment fragment) {
        getFragmentManager()
                .beginTransaction()
                .add(viewId, fragment)
                .commit();
    }
}

To prevent this pitfall, I'll update the gist and include this missing code. Thanks!

@ebraminio
Copy link

ebraminio commented Sep 26, 2018

Adopted it with DaggerApplication in this fork which simplifies things even more but differs noticeably on these two,

public class App extends DaggerApplication {
    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerAppComponent.builder().create(this);
    }
}

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class})
public interface AppComponent extends AndroidInjector<MainApplication> {
    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<MainApplication> {
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment