Skip to content

Instantly share code, notes, and snippets.

@ok3141
Forked from ronshapiro/dagger-android-view.md
Created April 10, 2018 09:35
Show Gist options
  • Save ok3141/3f7f14f580b4e6df74f71d4a6e87ebf1 to your computer and use it in GitHub Desktop.
Save ok3141/3f7f14f580b4e6df74f71d4a6e87ebf1 to your computer and use it in GitHub Desktop.
dagger.android for views in ~10 minutes

1. You can implement View.getActivity() like this:

public interface ViewWithActivity {
  // Using android-gradle-plugin 3.0, which has the desugar step for default methods on interfaces
  default Activity getActivity() {
    // https://android.googlesource.com/platform/frameworks/support/+/03e0f3daf3c97ee95cd78b2f07bc9c1be05d43db/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java#276
    Context context = getContext();
    while (context instanceof ContextWrapper) {
      if (context instanceof Activity) {
        return (Activity)context;
      }
      context = ((ContextWrapper) context).getBaseContext();
    }
    throw new IllegalStateException("Context does not stem from an activity: " + getContext());
  }
}

2. Create a parallel to the Has*Injector classes

interface HasViewInjector {
  AndroidInjector<View> viewInjector();
}

3. Define a new activity base class:

public abstract DaggerAppCompatWithViewActivityNamingIsFun
    extends DaggerAppCompatActivity
    implements HasViewInjector {
  @Inject DispatchingAndroidInjector<View> viewInjector;
  
  @Override
  public AndroidInjector<View> viewInjector() {
    return viewInjector;
  }
}

4. Create a module

@Module
interface ViewInjectionModule {
  @Multibinds
  abstract Map<Class<? extends View>, AndroidInjector.Factory<? extends View>>
      viewInjectorFactories();
}

// ...

@Component(
  modules = {
    AndroidSupportInjectionModule.class,
    ViewInjectionModule.class, 
    // ...
  })
interface AppComponent

5. ViewInjection.inject

public final class ViewInjection {
  public static void inject(ViewWithActivity view) {
   checkNotNull(view, "view");
    Activity activity = view.getActivity();
    if (!(application instanceof HasViewInjector)) {
      throw new RuntimeException(
          String.format(
              "%s does not implement %s",
              activity.getClass().getCanonicalName(),
              HasViewInjector.class.getCanonicalName()));
    }

    AndroidInjector<View> viewInjector =
        ((HasViewInjector) activity).viewInjector();
    checkNotNull(viewInjector, "%s.viewInjector() returned null", activity.getClass());

    viewInjector.inject(view);
  }
}

6. Equivalent of @ContributesAndroidInjector MyView

7. Call ViewInjection.inject(this)

I'm not sure where this should happen yet, perhaps onFinishInflate or onAttachedToWindow()?

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