1# 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 9 - [User's guide](https://google.github.io/dagger/users-guide) 10 11TODO: Add some links. 12 13## State of the world 14 15Dagger 2 has been turned on for SystemUI and a early first pass has been taken 16for converting everything in [Dependency.java](packages/systemui/src/com/android/systemui/Dependency.java) 17to use Dagger. Since a lot of SystemUI depends on Dependency, stubs have been added to Dependency 18to proxy any gets through to the instances provided by dagger, this will allow migration of SystemUI 19through a number of CLs. 20 21### How it works in SystemUI 22 23For the classes that we're using in Dependency and are switching to dagger, the 24equivalent dagger version is using `@Singleton` and therefore only has one instance. 25To have the single instance span all of SystemUI and be easily accessible for 26other components, there is a single root `@Component` that exists that generates 27these. The component lives in [SystemUIFactory](packages/systemui/src/com/android/systemui/SystemUIFactory.java) 28and is called `SystemUIRootComponent`. 29 30```java 31 32@Singleton 33@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class, 34 ContextHolder.class}) 35public interface SystemUIRootComponent { 36 @Singleton 37 Dependency.DependencyInjector createDependency(); 38} 39``` 40 41The root component is composed of root modules, which in turn provide the global singleton 42dependencies across all of SystemUI. 43 44- `ContextHolder` is just a wrapper that provides a context. 45 46- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI 47variants (like other form factors e.g. Car). 48 49- `DependencyBinder` creates the mapping from interfaces to implementation classes. 50 51- `DependencyProvider` provides or binds any remaining depedencies required. 52 53### Adding injection to a new SystemUI object 54 55Anything that depends on any `@Singleton` provider from SystemUIRootComponent 56should be declared as a `@Subcomponent` of the root component. This requires 57declaring your own interface for generating your own modules or just the 58object you need injected. The subcomponent also needs to be added to 59SystemUIRootComponent in SystemUIFactory so it can be acquired. 60 61```java 62public interface SystemUIRootComponent { 63+ @Singleton 64+ Dependency.DependencyInjector createDependency(); 65} 66 67public class Dependency extends SystemUI { 68 //... 69+ @Subcomponent 70+ public interface DependencyInjector { 71+ Dependency createSystemUI(); 72+ } 73} 74``` 75 76For objects which extend SystemUI and require injection, you can define an 77injector that creates the injected object for you. This other class should 78be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml). 79 80```java 81public static class DependencyCreator implements Injector { 82 @Override 83 public SystemUI apply(Context context) { 84 return SystemUIFactory.getInstance().getRootComponent() 85 .createDependency() 86 .createSystemUI(); 87 } 88} 89``` 90 91### Adding a new injectable object 92 93First tag the constructor with `@Inject`. Also tag it with `@Singleton` if only one 94instance should be created. 95 96```java 97@Singleton 98public class SomethingController { 99 @Inject 100 public SomethingController(Context context, 101 @Named(MAIN_HANDLER_NAME) Handler mainHandler) { 102 // context and mainHandler will be automatically populated. 103 } 104} 105``` 106 107If you have an interface class and an implementation class, dagger needs to know 108how to map it. The simplest way to do this is to add an `@Provides` method to 109DependencyProvider. The type of the return value tells dagger which dependency it's providing. 110 111```java 112public class DependencyProvider { 113 //... 114 @Singleton 115 @Provides 116 public SomethingController provideSomethingController(Context context, 117 @Named(MAIN_HANDLER_NAME) Handler mainHandler) { 118 return new SomethingControllerImpl(context, mainHandler); 119 } 120} 121``` 122 123If you need to access this from Dependency#get, then add an adapter to Dependency 124that maps to the instance provided by Dagger. The changes should be similar 125to the following diff. 126 127```java 128public class Dependency { 129 //... 130 @Inject Lazy<SomethingController> mSomethingController; 131 //... 132 public void start() { 133 //... 134 mProviders.put(SomethingController.class, mSomethingController::get); 135 } 136} 137``` 138 139### Using injection with Fragments 140 141Fragments are created as part of the FragmentManager, so they need to be 142setup so the manager knows how to create them. To do that, add a method 143to com.android.systemui.fragments.FragmentService$FragmentCreator that 144returns your fragment class. Thats all thats required, once the method 145exists, FragmentService will automatically pick it up and use injection 146whenever your fragment needs to be created. 147 148```java 149public interface FragmentCreator { 150+ NavigationBarFragment createNavigationBar(); 151} 152``` 153 154If you need to create your fragment (i.e. for the add or replace transaction), 155then the FragmentHostManager can do this for you. 156 157```java 158FragmentHostManager.get(view).create(NavigationBarFragment.class); 159``` 160 161### Using injection with Views 162 163Generally, you shouldn't need to inject for a view, as the view should 164be relatively self contained and logic that requires injection should be 165moved to a higher level construct such as a Fragment or a top-level SystemUI 166component, see above for how to do injection for both of which. 167 168Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a 169lot of code that could benefit from injection and will need to be migrated 170off from Dependency#get uses. Similar to how fragments are injected, the view 171needs to be added to the interface 172com.android.systemui.util.InjectionInflationController$ViewInstanceCreator. 173 174```java 175public interface ViewInstanceCreator { 176+ QuickStatusBarHeader createQsHeader(); 177} 178``` 179 180Presumably you need to inflate that view from XML (otherwise why do you 181need anything special? see earlier sections about generic injection). To obtain 182an inflater that supports injected objects, call InjectionInflationController#injectable, 183which will wrap the inflater it is passed in one that can create injected 184objects when needed. 185 186```java 187@Override 188public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 189 Bundle savedInstanceState) { 190 return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false); 191} 192``` 193 194There is one other important thing to note about injecting with views. SysUI 195already has a Context in its global dagger component, so if you simply inject 196a Context, you will not get the one that the view should have with proper 197theming. Because of this, always ensure to tag views that have @Inject with 198the @Named view context. 199 200```java 201public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs, 202 OtherCustomDependency something) { 203 //... 204} 205``` 206 207## Updating Dagger2 208 209Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded 210into 211[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/) 212 213 214## TODO List 215 216 - Eliminate usages of Dependency#get 217 - Add links in above TODO 218