You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
6.4 KiB
171 lines
6.4 KiB
7 months ago
|
# Dagger 2 in SystemUI
|
||
|
*Dagger 2 is a dependency injection framework that compiles annotations to code
|
||
|
to create dependencies without reflection*
|
||
|
|
||
|
## Recommended reading
|
||
|
|
||
|
Go read about Dagger 2.
|
||
|
|
||
|
- [User's guide](https://google.github.io/dagger/users-guide)
|
||
|
|
||
|
TODO: Add some links.
|
||
|
|
||
|
## State of the world
|
||
|
|
||
|
Dagger 2 has been turned on for SystemUI and a early first pass has been taken
|
||
|
for converting everything in [Dependency.java](packages/systemui/src/com/android/systemui/Dependency.java)
|
||
|
to use Dagger. Since a lot of SystemUI depends on Dependency, stubs have been added to Dependency
|
||
|
to proxy any gets through to the instances provided by dagger, this will allow migration of SystemUI
|
||
|
through a number of CLs.
|
||
|
|
||
|
### How it works in SystemUI
|
||
|
|
||
|
For the classes that we're using in Dependency and are switching to dagger, the
|
||
|
equivalent dagger version is using `@Singleton` and therefore only has one instance.
|
||
|
To have the single instance span all of SystemUI and be easily accessible for
|
||
|
other components, there is a single root `@Component` that exists that generates
|
||
|
these. The component lives in [SystemUIFactory](packages/systemui/src/com/android/systemui/SystemUIFactory.java)
|
||
|
and is called `SystemUIRootComponent`.
|
||
|
|
||
|
```java
|
||
|
|
||
|
@Singleton
|
||
|
@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
|
||
|
ContextHolder.class})
|
||
|
public interface SystemUIRootComponent {
|
||
|
@Singleton
|
||
|
Dependency.DependencyInjector createDependency();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The root component is composed of root modules, which in turn provide the global singleton
|
||
|
dependencies across all of SystemUI.
|
||
|
|
||
|
- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
|
||
|
variants (like other form factors e.g. Car).
|
||
|
|
||
|
- `DependencyBinder` creates the mapping from interfaces to implementation classes.
|
||
|
|
||
|
- `DependencyProvider` provides or binds any remaining depedencies required.
|
||
|
|
||
|
### Adding injection to a new SystemUI object
|
||
|
|
||
|
SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
|
||
|
information in that file to locate and construct an instance of the requested SystemUI class.
|
||
|
|
||
|
### Adding a new injectable object
|
||
|
|
||
|
First tag the constructor with `@Inject`. Also tag it with `@Singleton` if only one
|
||
|
instance should be created.
|
||
|
|
||
|
```java
|
||
|
@Singleton
|
||
|
public class SomethingController {
|
||
|
@Inject
|
||
|
public SomethingController(Context context,
|
||
|
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
|
||
|
// context and mainHandler will be automatically populated.
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
If you have an interface class and an implementation class, dagger needs to know
|
||
|
how to map it. The simplest way to do this is to add an `@Provides` method to
|
||
|
DependencyProvider. The type of the return value tells dagger which dependency it's providing.
|
||
|
|
||
|
```java
|
||
|
public class DependencyProvider {
|
||
|
//...
|
||
|
@Singleton
|
||
|
@Provides
|
||
|
public SomethingController provideSomethingController(Context context,
|
||
|
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
|
||
|
return new SomethingControllerImpl(context, mainHandler);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
If you need to access this from Dependency#get, then add an adapter to Dependency
|
||
|
that maps to the instance provided by Dagger. The changes should be similar
|
||
|
to the following diff.
|
||
|
|
||
|
```java
|
||
|
public class Dependency {
|
||
|
//...
|
||
|
@Inject Lazy<SomethingController> mSomethingController;
|
||
|
//...
|
||
|
public void start() {
|
||
|
//...
|
||
|
mProviders.put(SomethingController.class, mSomethingController::get);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Using injection with Fragments
|
||
|
|
||
|
Fragments are created as part of the FragmentManager, so they need to be
|
||
|
setup so the manager knows how to create them. To do that, add a method
|
||
|
to com.android.systemui.fragments.FragmentService$FragmentCreator that
|
||
|
returns your fragment class. Thats all thats required, once the method
|
||
|
exists, FragmentService will automatically pick it up and use injection
|
||
|
whenever your fragment needs to be created.
|
||
|
|
||
|
```java
|
||
|
public interface FragmentCreator {
|
||
|
NavigationBarFragment createNavigationBar();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
If you need to create your fragment (i.e. for the add or replace transaction),
|
||
|
then the FragmentHostManager can do this for you.
|
||
|
|
||
|
```java
|
||
|
FragmentHostManager.get(view).create(NavigationBarFragment.class);
|
||
|
```
|
||
|
|
||
|
### Using injection with Views
|
||
|
|
||
|
DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
|
||
|
|
||
|
Needing to inject objects into your View's constructor generally implies you
|
||
|
are doing more work in your presentation layer than is advisable.
|
||
|
Instead, create an injected controller for you view, inject into the
|
||
|
controller, and then attach the view to the controller after inflation.
|
||
|
|
||
|
View injection generally causes headaches while testing, as inflating a view
|
||
|
(which may in turn inflate other views) implicitly causes a Dagger graph to
|
||
|
be stood up, which may or may not contain the appropriately
|
||
|
faked/mocked/stubbed objects. It is a hard to control process.
|
||
|
|
||
|
## Updating Dagger2
|
||
|
|
||
|
We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
|
||
|
when that repository is updated.
|
||
|
|
||
|
*Deprecated:*
|
||
|
|
||
|
Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
|
||
|
into
|
||
|
[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
|
||
|
|
||
|
The following commands should work, substituting in the version that you are looking for:
|
||
|
|
||
|
````
|
||
|
cd prebuilts/tools/common/m2/repository/com/google/dagger/
|
||
|
|
||
|
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
|
||
|
|
||
|
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
|
||
|
|
||
|
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
|
||
|
|
||
|
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
|
||
|
````
|
||
|
|
||
|
Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
|
||
|
|
||
|
## TODO List
|
||
|
|
||
|
- Eliminate usages of Dependency#get
|
||
|
- Add links in above TODO
|