Dagger2 框架与 SystemUI

简介: Dagger2 框架与 SystemUI

Dagger2 注解基础


结构


Dagger2 要实现一个完整的依赖注入,必不可少的元素有三种,Module,Component,Container。

Container 就是可以被注入的容器,Container 拥有需要被初始化的元素。需要被初始化的元素必须标上 @Inject,只有被标上 @Inject 的元素才会被自动初始化。被注解的构造方法会自动编译生成一个Factory工厂类提供该类对象。

Module 可以说就是依赖的原材料的制造工厂,所有需要被注入的元素的实现都是从 Module 生产并且封装起来,然后交给注入器 Component。

Component 相当于中间的运输员,将 Module 中产生的依赖对象自动注入到 Container 中。


如下图:Container 就相当于 Activity/fragment;Component 相当于桥梁。


image.png


一些常用的注解

@Inject

用 @Inject 注解标注目标类中依赖类的实例对象

用 @Inject 注解标注依赖类的构造函数

若其他类还依赖于另外的类,则重复进行上面2个步骤

@Component

所有的 Component 都必须以接口形式定义,使用 @Component 注解的接口一般直接命名为 XXXComponent

Dagger2 框架将自动生成该 Component 的实现类,对应的类名是 DaggerXXXComponent

添加注入方法,一般使用 inject 做为方法名,方法参数为对应的 Container,调用该方法进行注入,有参数的方法返回值类型是void

注入方法也可以没有输入参数,但是必须有返回值,返回的实例会先从事先定义的 Module 中查找,如果找不到,则会使用该类中带 @Inject 的构造方法来生成,同时也会递归注入构造方法的参数以及带 @Inject 的成员变量

@Module

Module 是一个简单的工厂类,添加注解 @Module 注明本类属于 Module

Module 里面的方法都是创建相应类实例的方法

在 Module 中添加注解 @Provides 注明该方法是用来提供依赖对象的特殊方法

注:一个 Component 可以包含多个 Module ,这样 Component 获取依赖时候会自动从多个 Module 中查找获取,Module 间不能有重复方法。添加多个 Module 有两种方法,一种是在 Component 的注解 @Component(modules={×××.class,×××.class}) 添加多个 modules,另外一种是在可以被使用的 Module 标签中的添加其他 modules: @Module(includes={×××.class,×××.class})

@Provides

通过 @Provides 标注该 Module 可以向外界提供的类的实例对象的方法

Module 中 @Provides 方法可以带输入参数,其参数由 Module 集合中的其他 @Provides 方法的返回值提供

如果2中找不到 @Provides 方法提供对应参数的对象,则其会自动查找参数类中带 @Inject 的无参构造生成对象

@Binds

使用 @Inject 初始化对象时如果需要初始化的是接口的实例,这时候就需要用到 @Binds

方法中必须有且只有一个参数,必须是接口的实现类

实现类必须提供 @Inject 的构造或 Module 中以 @Provides 形式提供

@Binds 相当于是在接口或者抽象类中代替 @Provides

@Named

@Named 是基于 String 的限定符

有两个相同的依赖(都继承某一个父类或者都实现某一个接口)可以提供,这个时候就需要用 @Named 来区分,使用方法为 @Named(“AAA”),@Named(“BBB”)

拥有相同 @Named 的 @Inject 成员变量与 @Provides 方法才能被对应起来使用

@Qualifier

@Qualifier 用来注解接口作为自定义区分标志

使用此注解的接口 xxx.java 可以当做注解标签进行使用,@xxx

@Singleton与@Scope

顾名思义使用 @Singleton 是用来标注单例对象的

在 Component 里的方法添加 @Singleton,同时在 Module 对应的 @Provides 方法上也添加 @Singleton,这样一来每次注入的都会是同一个对象

@Scope 是注解的注解, Scope机制可以保证在 Scope 标记的 Component 作用域内 ,类会保持单例,@Singleton 是 @Scope 的一个默认实现

@Subcomponent

@Subcomponent 用于拓展原有 Component,它与 @Component(dependencies=XXComponent.classs) 类似

@Subcomponent 注解不会在编译后生成Dagger前缀的容器类,而是体现在父 Component 的私有内部类

Subcomponent 既拥有父 Component 拥有的 Scope,也拥有自己的 Scope

在父 Component 中显式添加添加返回 SubComponent 的方法后,继承关系才成立,SubComponent 能自动在父 Component 中查找缺失的依赖

注入方式

参考官网文章

Direct:在调用print之前,inject已经完成了,对象也已经被生成好了,provideInteger()已经被调用过了。

Provider:保证每次重新加载,但是并不意味着每次返回的对象都是不同的,只有Module的Provide方法每次都创建新实例时,Provider每次get()的对象才不相同。

Lazy:在调用Lazy.get()时生成新的Integer对象,并且保存当前对象,下次调用同一个Lazy对象的get()方法会返回之前同样的对象。

Lazy不等同于Singleton,Lazy只在当前Lazy对象保证唯一,如果有两个不同的Lazy对象,那么他们get()的对象也是不同的。

Provider和Lazy都是用于包装Container中需要被注入的类型,Lazy用于延迟加载,Provide用于强制重新加载。

@Module
   final class CounterModule {
     int next = 100;
      @Provides Integer provideInteger() {
       System.out.println("computing...");
       return next++;
     }
   }
// Direct Injection
// This class injects that integer and prints it 3 times:
   final class DirectCounter {
      @Inject Integer value;
     void print() {
       System.out.println("printing...");
       System.out.println(value);
       System.out.println(value);
       System.out.println(value);
     }
   }
// Injecting a DirectCounter and invoking print() reveals that the value is computed before it is required:
/**   
 *    computing...
 *    printing...
 *    100
 *    100
 *    100
 */
// Provider Injection
// This class injects a [provider](https://docs.oracle.com/javaee/7/api/javax/inject/Provider.html?is-external=true "class or interface in javax.inject") for the integer. It calls `Provider.get()` 3 times and prints each result:
   final class ProviderCounter {
      @Inject Provider<Integer> provider;
     void print() {
       System.out.println("printing...");
       System.out.println(provider.get());
       System.out.println(provider.get());
       System.out.println(provider.get());
     }
   }
// Injecting a `ProviderCounter` and invoking `print()` shows that a new value is computed each time `Provider.get()` is used:
/**   
 *    printing...
 *    computing...
 *    100
 *    computing...
 *    101
 *    computing...
 *    102
 */
// Lazy Injection
// This class injects a Lazy for the integer. Like the provider above, it calls Lazy.get() 3 times and prints each result:
   final class LazyCounter {
      @Inject Lazy<Integer> lazy;
     void print() {
       System.out.println("printing...");
       System.out.println(lazy.get());
       System.out.println(lazy.get());
       System.out.println(lazy.get());
     }
   }
// Injecting a LazyCounter and invoking print() shows that a new value is computed immediately before it is needed. The same value is returned for all subsequent uses:
/**   
 *    printing...
 *    computing...
 *    100
 *    100
 *    100
 */
// Lazy != Singleton
// Note that each injected Lazy is independent, and remembers its value in isolation of other Lazy instances. In this example, two LazyCounter objects are created and print() is called on each:
   final class LazyCounters {
      @Inject LazyCounter counter1;
      @Inject LazyCounter counter2;
     void print() {
       counter1.print();
       counter2.print();
     }
   }
// The output demonstrates that each Lazy works independently:
/**   
 *    printing...
 *    computing...
 *    100
 *    100
 *    100
 *    printing...
 *    computing...
 *    101
 *    101
 *    101
 * /

Dagger2 导航


Android Studio 4.1 Canary 7 之后 针对 Dagger2 的导航进行了支持,方便开发者快速回溯依赖关系。

image.png


  • 点击向上的箭头可以查看该实例注入的提供方
  • 点击向下的树形图会转到或展开该实例被用作依赖项的位置或列表

Dagger2 在 SystemUI 里的应用

SystemUI里面大量的模块都使用了此注入方式,比如StatusBar,QS,Keyguard等。

下面以 Android 10.0  源码的SystemUI来详细说明。

定义清单列表

SystemUI的application标签定义了一个appComponentFactory属性

<application
    android:name=".SystemUIApplication"
    ...
    tools:replace="android:appComponentFactory"
    android:appComponentFactory=".SystemUIAppComponentFactory">
    ...
    <service ...
    />
    <activity ...
    />
    <reveiver ...
    />
    ...
</application>

这个属性是用来控制manifest清单文件里的组件的初始化,在manifest清单文件里的组件构建对象时会调用下面这些方法。


image.png


启动Application

随着SystemServer发出启动SystemUIService的请求,SystemUI的Application将首先被实例化。在实例化之前,指定的AppComponentFactory实现类将会收到回调。

1、调用super得到Application实例之后向其注册Context准备完毕的回调,该回调会执行SystemUIFactory和各组件的初始化:

// SystemUIAppComponentFactory.java 
public SystemUIAppComponentFactory() {
        super();
    }
public class SystemUIAppComponentFactory extends AppComponentFactory {
    @Inject
    public ContextComponentHelper mComponentHelper;
    ...
    @Override
    public Application instantiateApplicationCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Application app = super.instantiateApplicationCompat(cl, className);
        if (app instanceof ContextInitializer) {
            // 注册Context成功取得的回调
            ((ContextInitializer) app).setContextAvailableCallback(
                    context -> {
                        SystemUIFactory.createFromConfig(context);
                        // 注入SystemUIAppComponentFactory对象
                        SystemUIFactory.getInstance().getRootComponent().inject(
                                SystemUIAppComponentFactory.this);
                    }
            );
        }
        return app;
    }
    ...
}

2、在SystemUIApplication onCreate时执行上述回调, 构建SystemUIFactory,并对它进行初始化:

// SystemUIApplication.java
public class SystemUIApplication extends Application implements
        SystemUIAppComponentFactory.ContextInitializer {
    ...
    @Override
    public void setContextAvailableCallback(
            SystemUIAppComponentFactory.ContextAvailableCallback callback) {
        mContextAvailableCallback = callback;
    }
    @Override
    public void onCreate() {
        ...
        // 执行回调
        mContextAvailableCallback.onContextAvailable(this);
        // 获取 SystemUIRootComponent,很多组件都是通过它注入的
        mRootComponent = SystemUIFactory.getInstance().getRootComponent();
        // 通过 SystemUIRootComponent 定义的注入方法获得 ContextComponentHelper 对象
        mComponentHelper = mRootComponent.getContextComponentHelper();
        ...
    }
}

3、创建SystemUIFactory实例,并初始化SystemUI 的Dagger组件,并向Dependency实例注入依赖:

// SystemUIFactory.java
public class SystemUIFactory {
    ...
    public static void createFromConfig(Context context) {
        ...
        try {
            Class<?> cls = null;
            cls = context.getClassLoader().loadClass(clsName);
            // 创建SystemUIFactory实例
            mFactory = (SystemUIFactory) cls.newInstance();
            // 初始化SystemUIFactory
            mFactory.init(context);
        }
    }
    private void init(Context context) {
        // 取得SystemUI的Dagger组件实例
        mRootComponent = buildSystemUIRootComponent(context);
        // 创建Dependency实例
        Dependency dependency = new Dependency();
        // 将Dependency绑定到子组件DependencyInjector中,这里会通过Component将各类依赖注入Dependency
        mRootComponent.createDependency().createSystemUI(dependency);
        // 初始化Dependency
        dependency.start();
    }
    // 初始化Dagger组件
    protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
        return DaggerSystemUIRootComponent.builder() 
                .dependencyProvider(new DependencyProvider())
                .contextHolder(new ContextHolder(context))
                .build();
    }
    ...
}

4、Dependency里掌管着各式各样的依赖,被依赖的各实例通过Map管理。每个依赖都通过LazyDependencyCreator将各实例对应的懒加载回调缓存进去,然后保存在Map mProviders里。其后在各实例真正使用时调用createDependency() 进行创建:

// Dependency.java
public class Dependency {
    // 使用class作为key将对应实例缓存
    private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
    // 同样的class的key缓存实例的懒加载回调
    private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();
    protected void start() {
        // 构建LazyDependencyCreator放入mProviders,回调取得对应实例
        mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
        mProviders.put(BG_LOOPER, mBgLooper::get);
        mProviders.put(MAIN_LOOPER, mMainLooper::get);
        mProviders.put(MAIN_HANDLER, mMainHandler::get);
        mProviders.put(ActivityStarter.class, mActivityStarter::get);
        mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
        ...
    }
    /**
     * @deprecated see docs/dagger.md
     *  获取xxx类实例
     */
    @Deprecated
    public static <T> T get(Class<T> cls) {
        /* UNISOC: Bug 1169828 sDependency may be null @{ */
        if (sDependency == null) {
            Log.d(TAG, "get sDependency is null");
            return null;
        }
        return sDependency.getDependency(cls);
    }
    // 根据class查询 mDependencies里的缓存
    private synchronized <T> T getDependencyInner(Object key) {
        T obj = (T) mDependencies.get(key);
        if (obj == null) {
            // 尚未缓存的话通过懒加载回调获取注入的实例并缓存进 mDependencies
            obj = createDependency(key);
            mDependencies.put(key, obj);
            if (autoRegisterModulesForDump() && obj instanceof Dumpable) {
                mDumpManager.registerDumpable(obj.getClass().getName(), (Dumpable) obj);
            }
        }
        return obj;
    }
    // 根据class查询 mProviders里的懒加载回调
    protected <T> T createDependency(Object cls) {
        Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
        LazyDependencyCreator<T> provider = mProviders.get(cls);
        if (provider == null) {
            throw new IllegalArgumentException("Unsupported dependency " + cls
                    + ". " + mProviders.size() + " providers known.");
        }
        // 里面的回调实现已经通过start()里的mProviders.put存入了,实际调用的就是各类Lazy.get()
        return provider.createDependency();
    }
    private interface LazyDependencyCreator<T> {
        // 封装好的回调
        T createDependency();
    }
}

启动SystemUIService

创建好Application后,会启动SystemUIService这个主Service,然后逐个启动其他组件。

// SystemUIService.java
public class SystemUIService extends Service {
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        // Start all of SystemUI
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        ...
    }
    ...
}

启动各类服务组件

先看一张SystemUI的Component关系图:


image.png

回到 SystemUIApplication

public class SystemUIApplication {
    ...
    public void startServicesIfNeeded() {
        // 各services定义在 R.array.config_systemUIServiceComponents 里面
        String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
        startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
    }
    private void startServicesIfNeeded(String metricsPrefix, String[] services) {
        ...
        final int N = services.length;
        for (int i = 0; i < N; i++) {
            String clsName = services[i];
            ...
            try {
                // 通过class name从ContextComponentHelper中获取对应组件的实例
                SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
                if (obj == null) {
                    Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
                    obj = (SystemUI) constructor.newInstance(this);
                }
                mServices[i] = obj;
            } catch (ClassNotFoundException
                    | NoSuchMethodException
                    | IllegalAccessException
                    | InstantiationException
                    | InvocationTargetException ex) {
                throw new RuntimeException(ex);
            }
            if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
            // 调用各组件实现的start()方法
            mServices[i].start();
            ...
        }
        ...
    }
}

mComponentHelper是ContextComponentHelper的对象,下面讲解一下它的注入过程。

在 SystemUIRootComponent 中定义了 ContextComponentHelper 单例对象的获取方法:

// SystemUIRootComponent.java
@Singleton
@Component(modules = {
        ...
        SystemUIModule.class,
        SystemUIDefaultModule.class})
public interface SystemUIRootComponent {
    ...
    @Singleton
    ContextComponentHelper getContextComponentHelper();
    ...
}

在上面介绍@Component时,有提到:无参有返回值的方法,实例从定义的Module中查找,这里在 SystemUIModule 里可以找到:

@Module(includes = {
            ...
        },
        subcomponents = {StatusBarComponent.class,
                NotificationRowComponent.class,
                ExpandableNotificationRowComponent.class})
public abstract class SystemUIModule {
    ...
    @Binds
    public abstract ContextComponentHelper bindComponentHelper(
            ContextComponentResolver componentHelper);
    ...
}

由于该方法标了abstract并没有进行实现,而 ContextComponentResolver 对象,它是 ContextComponentHelper 的实现类,所以实例对象会通过 ContextComponentResolver 里面带 @Inject 标签的构造方法来生成。

@Singleton
public class ContextComponentResolver implements ContextComponentHelper {
    private final Map<Class<?>, Provider<Activity>> mActivityCreators;
    private final Map<Class<?>, Provider<Service>> mServiceCreators;
    private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
    private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
    private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;
    @Inject
    ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
            Map<Class<?>, Provider<Service>> serviceCreators,
            Map<Class<?>, Provider<SystemUI>> systemUICreators,
            Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
            Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
        mActivityCreators = activityCreators;
        mServiceCreators = serviceCreators;
        mSystemUICreators = systemUICreators;
        mRecentsCreators = recentsCreators;
        mBroadcastReceiverCreators = broadcastReceiverCreators;
    }
    ...
    /**
     * Looks up the SystemUI class name to see if Dagger has an instance of it.
     */
    @Override
    public SystemUI resolveSystemUI(String className) {
        return resolve(className, mSystemUICreators);
    }
    private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
        try {
            Class<?> clazz = Class.forName(className);
            Provider<T> provider = creators.get(clazz);
            return provider == null ? null : provider.get();
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}

这里的构造方法由于带了 @Inject,所以它的参数Map都会一起注入进去。但在这里又会遇到一个问题:这些Map哪里来的?

这里就需要提到:dagger2 的 MultiBindings。

首先在 Module 中多次定义一系列返回值类型相同的方法,这里以 SystemUIBinder 为例,如果需要的类型是Map,那么就使用 @IntoMap 和 对应key的 @MapKey 的实现 @ClassKey,@IntoMap 表示这些方法的返回值对象都要装入同一个Map集合中,@ClassKey 表示Map的key为class。

然后还必须加上 @Provides 标签,如果是接口或抽象类则用 @Binds。这样声明下来,dagger2 会自动为其注入 Map<Class<?>, Provider>,即把上面的 systemUICreators 给注入进去了。

下面以 SystemUIBinder 为例:

// SystemUIBinder.java
@Module(includes = {RecentsModule.class, StatusBarModule.class, BubbleModule.class,
        KeyguardModule.class})
public abstract class SystemUIBinder {
    /** Inject into AuthController. */
    @Binds
    @IntoMap
    @ClassKey(AuthController.class)
    public abstract SystemUI bindAuthController(AuthController service);
    /** Inject into Divider. */
    @Binds
    @IntoMap
    @ClassKey(Divider.class)
    public abstract SystemUI bindDivider(Divider sysui);
    ...
    /** Inject into StatusBar. */
    @Binds
    @IntoMap
    @ClassKey(StatusBar.class)
    public abstract SystemUI bindsStatusBar(StatusBar sysui);
    ...
}

接着看 参数 StatusBar 的实例又是由 StatusBarPhoneModule 提供的:

// StatusBarPhoneModule.java
@Module(includes = {StatusBarPhoneDependenciesModule.class})
public interface StatusBarPhoneModule {
    /**
     * Provides our instance of StatusBar which is considered optional.
     */
    @Provides
    @Singleton
    static StatusBar provideStatusBar(
            Context context,
            NotificationsController notificationsController,
            LightBarController lightBarController,
            AutoHideController autoHideController,
            KeyguardUpdateMonitor keyguardUpdateMonitor,
            StatusBarIconController statusBarIconController,
            ...) {
        return new StatusBar(
                context,
                notificationsController,
                lightBarController,
                autoHideController,
                keyguardUpdateMonitor,
                statusBarIconController,
                ...);
    }
}

new StatusBar 对象所用到的参数,有些是通过内部自身带 @Inject 标签的构造方法注入,有些则是又再通过其他 Module 提供,这里就不再往下继续跟踪了。

到此,config_systemUIServiceComponents 里面定义的组件的实例对象注入过程就结束了。

相关文章
|
2月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
77 8
|
6月前
|
容器
Dagger2的使用
Dagger2的使用
42 0
|
缓存 Android开发 开发者
Dagger2和它在SystemUI上的应用
Dagger2和它在SystemUI上的应用
Dagger2和它在SystemUI上的应用
|
测试技术 数据库 Android开发
Android Jetpack 浅析Hilt依赖注入
首先,某个类的成员变量称为依赖,如若此变量想要实例化引用其类的方法,可以通过构造函数传参或者通过某个方法获取对象,此等通过外部方法获取对象实例的称为依赖注入;而依赖注入又可以简单分为`手动注入`和`自动注入`两种方式;`Hilt`就是基于Dagger进行`场景化优化`的一个依赖注入库,Hilt是Google专门为Android平台打造的一个依赖注入库,在使用上极大程度进行啦简化(与dagger相比)
406 1
Android | 依赖注入与 Dagger2 框架【@Deprecated】
Android | 依赖注入与 Dagger2 框架【@Deprecated】
209 0
Android | 依赖注入与 Dagger2 框架【@Deprecated】
|
存储 前端开发 Java
Jetpack 系列(10)—— 从 Dagger2 到 Hilt 玩转依赖注入(一)
Jetpack 系列(10)—— 从 Dagger2 到 Hilt 玩转依赖注入(一)
439 0
Jetpack 系列(10)—— 从 Dagger2 到 Hilt 玩转依赖注入(一)
|
前端开发 Android开发 开发者
Dagger Hilt - Android官方推荐的依赖注入框架
Dagger Hilt 帮助 Android 项目实现依赖注入
587 0
|
测试技术 API Android开发
espresso框架
一、espresso简介espresso是google官方推出的ui自动化框架,可以用来做单元测试和自动化测试。 官方说明文档:https://developer.android.com/training/testing/espresso/ 官方中文文档:https://lovexiaov.
1585 0
|
测试技术 Android开发 数据格式
Android Dagger2依赖注入
Dagger2项目主页 使用依赖 annotationProcessor 'com.google.dagger:dagger-compiler:2.
1039 0