Skip to content

Dagger2

Weiyi Li edited this page Apr 1, 2018 · 23 revisions

Injecting Activity objects

commit

Injecting Fragment objects | commit

  1. its host Activity extends DaggerAppCompatActivity;

  2. it extends DaggerFragment;

  3. Use @Inject to annotate fields of this fragment that REQUIRE dependency, then delete relates code that create the fields instance;

  4. Use @Inject to annotate constructors that PROVIDE dependency, or

  5. Create a @Module annotated class, and within this class use an @Provides annotated method to provide a dependency.

  6. Use ContributesAndroidInjector to install module to dagger graph, because the fragment must be hosted by one activity, that means the activity should also be added to dagger graph:

    This annotation must be applied to an abstract method in a {@link dagger.Module} that returns a concrete Android framework type (e.g. Activity, Fragment, Service, etc). The method should have no parameters.

    /**
     * ArticleListFragment is hosted by ArticleListActivity, so we need to
     * install the fragment's module to this activity.
     */
    @ContributesAndroidInjector(modules = { ArticleListActivityHostedFragment.class })
    abstract ArticleListActivity bindArticleListActivity();
    
    /**
     * The reason to create this class is that this activity maybe host many fragments.
     */
    @Module
    abstract class ArticleListActivityHostedFragment {
    
        @ContributesAndroidInjector(modules = ArticleListFragmentModule.class)
        abstract ArticleListFragment bindArticleListFragment();
    
        // another fragment ...
    }

@Inject doesn’t work everywhere to provide dependency

  • Interfaces can’t be constructed.
  • Third-party classes can’t be annotated.
  • Configurable objects must be configured!

Add @Inject to constructor to provide dependency, instead of defining an unnecessary @Provides annotation | commit

Because @Inject-annotated constructor works here !

@Module 
public class ArticleListFragmentModule {
     ArticleListFragmentViewModel provideArticleListFragmentViewModel(ArticleListFragment fragment, ArticleListViewModelFactory factory) {
         return ViewModelProviders.of(fragment, factory).get(ArticleListFragmentViewModel.class);
     }
-    @Provides
-    ArticleListViewModelFactory provideArticleListViewModelFactory(Context context, DemoRepository repository) {
-        return new ArticleListViewModelFactory(repository);
-    }
 }

 public class ArticleListViewModelFactory extends ViewModelProvider.NewInstanceFactory {
     private final DemoRepository mRepository;
+    @Inject
     public ArticleListViewModelFactory(DemoRepository repository) {
         mRepository = repository;
     }

Remove the parameters of constructor which the fields can be injected | commit | commit

simplify

 public class ArticleListViewModelFactory extends ViewModelProvider.NewInstanceFactory {
-    private final DemoRepository mRepository;
+    @Inject
+    DemoRepository mRepository;
 
     @Inject
-    public ArticleListViewModelFactory(DemoRepository repository) {
-        mRepository = repository;
+    public ArticleListViewModelFactory() {
     }

This is another good code snippet to show the advantage of dagger. With dagger, we can simplify the constructor to a default one without any parameters. The fields which need to be initialized in constructor, just @Inject it. If we need to provide a singleton instance, just @Singleton it, instead of using a static filed to hold the instance.

One of three types of dependency injection, WIKI- Constructor injection: This method requires the client to provide a parameter in a constructor for the dependency.

A class contains constructors that are invoked to create objects from the class blueprint. Java Doc - Providing Constructors for Your Classes

@Singleton
public class DemoRepository { 
-    private static final Object LOCK = new Object();
-    private static DemoRepository sInstance;
-    private final ArticleDao mArticleDao;
-    private final DemoWebService mDemoWebService;
-    private final AppExecutors mExecutors;
-    private final Context mContext;
+    @Inject
+    ArticleDao mArticleDao;
+    @Inject
+    DemoWebService mDemoWebService;
+    @Inject
+    AppExecutors mExecutors;
+    @Inject
+    Context mContext;
+
     private RateLimiter<String> repoListRateLimit = new RateLimiter<>(2, TimeUnit.MINUTES);

-    public DemoRepository(
-            Context context, ArticleDao articleDao, DemoWebService demoWebService, AppExecutors executors) {
-        mContext = context;
-        mArticleDao = articleDao;
-        mDemoWebService = demoWebService;
-        mExecutors = executors;
-    }
-
-    public synchronized static DemoRepository getInstance(
-            Context context, ArticleDao articleDao, DemoWebService demoWebService, AppExecutors executors) {
-        Log.d(LOG_TAG, "Getting the repository");
-        if (sInstance == null) {
-            synchronized (LOCK) {
-                if (sInstance == null) {
-                    sInstance = new DemoRepository(context, articleDao, demoWebService, executors);
-                    Log.d(LOG_TAG, "Made new repository");
-                }
-            }
-        }
-        return sInstance;
+    @Inject
+    public DemoRepository(){
     }

Provide dependency for Configurable objects must be configured! | commit

But sometimes the object need to be configured, for example, it needs exactly integer instead of another instance:

public class DemoRepository {
-    private RateLimiter<String> repoListRateLimit = new RateLimiter<>(2, TimeUnit.MINUTES);
+    @Inject
+    RateLimiter<String> repoListRateLimit;
}

public class AppModule {
+    @Provides
+    RateLimiter<String> provideRateLimiter() {
+        return new RateLimiter<>(2, TimeUnit.MINUTES);
+    }
}

Refer

The following 3 articles introduce how to use Dagger2 in a real Android App Project.

In part1, to attach activities/fragments to dagger graph, it create a lot boilerplate classes and needs a lot repetitive tasks, so is too complicated, so just walk through part1.

In part2, use @ContributesAndroidInjector to simplify dagger graph, check this commit for details

In part3, use DaggerActivity, DaggerFragment, DaggerApplication to reduce boilerplate in your Activity/Fragment/Application, use AndroidInjector<T> in your dagger components to reduce boilerplate too.

New Android Injector with Dagger 2 — part 1, part 2, part 3

Clone this wiki locally