blob: 8bfa1c28d9854e4b5ec0ab4807c88be3760f1c55 [file] [log] [blame] [view]
Jason Monk27d01a622018-12-10 15:57:09 -05001# Dagger 2 in SystemUI
2*Dagger 2 is a dependency injection framework that compiles annotations to code
3to create dependencies without reflection*
4
5## Recommended reading
6
7Go read about Dagger 2.
8
9TODO: Add some links.
10
11## State of the world
12
13Dagger 2 has been turned on for SystemUI and a early first pass has been taken
14for converting everything in Dependency.java to use Dagger. Since a lot of
15SystemUI depends on Dependency, stubs have been added to Dependency to proxy
16any gets through to the instances provided by dagger, this will allow migration
17of SystemUI through a number of CLs.
18
19### How it works in SystemUI
20
21For the classes that we're using in Dependency and are switching to dagger, the
22equivalent dagger version is using @Singleton and only having one instance.
23To have the single instance span all of SystemUI and be easily accessible for
24other components, there is a single root Component that exists that generates
25these. The component lives in SystemUIFactory and is called SystemUIRootComponent.
26
27```java
28@Singleton
29@Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
30public interface SystemUIRootComponent {
31 @Singleton
32 Dependency.DependencyInjector createDependency();
33}
34```
35
36The root modules are what provides the global singleton dependencies across
37SystemUI. ContextHolder is just a wrapper that provides a context.
38SystemUIFactory @Provide dependencies that need to be overridden by SystemUI
39variants (like other form factors). DependencyProvider provides or binds any
40remaining depedencies required.
41
42### Adding injection to a new SystemUI object
43
44Anything that depends on any @Singleton provider from SystemUIRootComponent
45should be declared as a Subcomponent of the root component, this requires
46declaring your own interface for generating your own modules or just the
47object you need injected. The subcomponent also needs to be added to
48SystemUIRootComponent in SystemUIFactory so it can be acquired.
49
50```java
51public interface SystemUIRootComponent {
52+ @Singleton
53+ Dependency.DependencyInjector createDependency();
54}
55
56public class Dependency extends SystemUI {
57 ...
58+ @Subcomponent
59+ public interface DependencyInjector {
60+ Dependency createSystemUI();
61+ }
62}
63```
64
65For objects that extend SystemUI and require injection, you can define an
66injector that creates the injected object for you. This other class should
67be referenced in @string/config_systemUIServiceComponents.
68
69```java
70public static class DependencyCreator implements Injector {
71 @Override
72 public SystemUI apply(Context context) {
73 return SystemUIFactory.getInstance().getRootComponent()
74 .createDependency()
75 .createSystemUI();
76 }
77}
78```
79
80### Adding a new injectable object
81
82First tag the constructor with @Inject. Also tag it with @Singleton if only one
83instance should be created.
84
85```java
86@Singleton
87public class SomethingController {
88 @Inject
89 public SomethingController(Context context,
90 @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
91 // context and mainHandler will be automatically populated.
92 }
93}
94```
95
96If you have an interface class and an implementation class, dagger needs to know
97how to map it. The simplest way to do this is to add a provides method to
98DependencyProvider.
99
100```java
101public class DependencyProvider {
102 ...
103 @Singleton
104 @Provide
105 public SomethingController provideSomethingController(Context context,
106 @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
107 return new SomethingControllerImpl(context, mainHandler);
108 }
109}
110```
111
112If you need to access this from Dependency#get, then add an adapter to Dependency
113that maps to the instance provided by Dagger. The changes should be similar
114to the following diff.
115
116```java
117public class Dependency {
118 ...
119 @Inject Lazy<SomethingController> mSomethingController;
120 ...
121 public void start() {
122 ...
123 mProviders.put(SomethingController.class, mSomethingController::get);
124 }
125}
126```
127
Jason Monk9424af72018-12-19 14:17:26 -0500128### Using injection with Fragments
129
130Fragments are created as part of the FragmentManager, so they need to be
131setup so the manager knows how to create them. To do that, add a method
132to com.android.systemui.fragments.FragmentService$FragmentCreator that
133returns your fragment class. Thats all thats required, once the method
134exists, FragmentService will automatically pick it up and use injection
135whenever your fragment needs to be created.
136
137```java
138public interface FragmentCreator {
139+ NavigationBarFragment createNavigationBar();
140}
141```
142
143If you need to create your fragment (i.e. for the add or replace transaction),
144then the FragmentHostManager can do this for you.
145
146```java
147FragmentHostManager.get(view).create(NavigationBarFragment.class);
148```
149
Jason Monk27d01a622018-12-10 15:57:09 -0500150## TODO List
151
152 - Eliminate usages of Depndency#get
153 - Add support for Fragments to handle injection automatically
154 - (this could be through dagger2-android or something custom)
155 - Reduce number of things with @Provide in DependencyProvider (many can be
156 @Inject instead)
157 - Migrate as many remaining DependencyProvider instances to @Bind
158 - Add links in above TODO