SystemUI 与 Dagger2 框架

Dagger2 注解基础

结构

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

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

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

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

一些常用的注解

@Inject
  1. 用 @Inject 注解标注目标类中依赖类的实例对象
  2. 用 @Inject 注解标注依赖类的构造函数
  3. 若其他类还依赖于另外的类,则重复进行上面2个步骤
@Component
  1. 所有的 Component 都必须以接口形式定义,使用 @Component 注解的接口一般直接命名为 XXXComponent
  2. Dagger2 框架将自动生成该 Component 的实现类,对应的类名是 DaggerXXXComponent
  3. 添加注入方法,一般使用 inject 做为方法名,方法参数为对应的 Container,调用该方法进行注入,有参数的方法返回值类型是void
  4. 注入方法也可以没有输入参数,但是必须有返回值,返回的实例会先从事先定义的 Module 中查找,如果找不到,则会使用该类中带 @Inject 的构造方法来生成,同时也会递归注入构造方法的参数以及带 @Inject 的成员变量
@Module
  1. Module 是一个简单的工厂类,添加注解 @Module 注明本类属于 Module
  2. Module 里面的方法都是创建相应类实例的方法
  3. 在 Module 中添加注解 @Provides 注明该方法是用来提供依赖对象的特殊方法

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

@Provides
  1. 通过 @Provides 标注该 Module 可以向外界提供的类的实例对象的方法
  2. Module 中 @Provides 方法可以带输入参数,其参数由 Module 集合中的其他 @Provides 方法的返回值提供
  3. 如果2中找不到 @Provides 方法提供对应参数的对象,则其会自动查找参数类中带 @Inject 的无参构造生成对象
@Binds
  1. 使用 @Inject 初始化对象时如果需要初始化的是接口的实例,这时候就需要用到 @Binds
  2. 方法中必须有且只有一个参数,必须是接口的实现类
  3. 实现类必须提供 @Inject 的构造或 Module 中以 @Provides 形式提供
  4. @Binds 相当于是在接口或者抽象类中代替 @Provides
@Named
  1. @Named 是基于 String 的限定符
  2. 有两个相同的依赖(都继承某一个父类或者都实现某一个接口)可以提供,这个时候就需要用 @Named 来区分,使用方法为 @Named("AAA"),@Named("BBB")
  3. 拥有相同 @Named 的 @Inject 成员变量与 @Provides 方法才能被对应起来使用
@Qualifier
  1. @Qualifier 用来注解接口作为自定义区分标志
  2. 使用此注解的接口 xxx.java 可以当做注解标签进行使用,@xxx
@Singleton与@Scope
  1. 顾名思义使用 @Singleton 是用来标注单例对象的
  2. 在 Component 里的方法添加 @Singleton,同时在 Module 对应的 @Provides 方法上也添加 @Singleton,这样一来每次注入的都会是同一个对象
  3. @Scope 是注解的注解, Scope机制可以保证在 Scope 标记的 Component 作用域内 ,类会保持单例,@Singleton 是 @Scope 的一个默认实现
@Subcomponent
  1. @Subcomponent 用于拓展原有 Component,它与 @Component(dependencies=XXComponent.classs) 类似
  2. @Subcomponent 注解不会在编译后生成Dagger前缀的容器类,而是体现在父 Component 的私有内部类
  3. Subcomponent 既拥有父 Component 拥有的 Scope,也拥有自己的 Scope
  4. 在父 Component 中显式添加添加返回 SubComponent 的方法后,继承关系才成立,SubComponent 能自动在父 Component 中查找缺失的依赖

注入方式

Provider与Lazy

Provider和Lazy都是用于包装Container中需要被注入的类型,Lazy用于延迟加载,Provide用于强制重新加载。
Provider保证每次重新加载,但是并不意味着每次返回的对象都是不同的,只有Module的Provide方法每次都创建新实例时,Provider每次get()的对象才不相同。
Lazy != Singleton,如果有两个不同的Lazy对象,那么他们get()的对象也是不同的。

示例:

    @Module
    final class CounterModule {
        int next = 100;

        @Provides
        Integer provideInteger() {
            System.out.println("computing...");
            return next++;
        }
    }

    // Provider 注入
    final class ProviderCounter {
        @Inject Provider provider;
        void print() {
            System.out.println("printing...");
            System.out.println(provider.get());
            System.out.println(provider.get());
            System.out.println(provider.get());
    }
    /**    
     *    打印结果:
     *     printing...
     *     computing...
     *     100
     *     computing...
     *     101
     *     computing...
     *     102
     */

    // Lazy 注入
    final class LazyCounter {
        @Inject Lazy Lazy;
        void print() {
            System.out.println("printing...");
            System.out.println(Lazy.get());
            System.out.println(Lazy.get());
            System.out.println(Lazy.get());
    }
    /**    
     *    打印结果:
     *     printing...
     *     computing...
     *     100
     *     100
     *     100
     */

Dagger2 导航

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


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

Dagger2 在 SystemUI 里的应用

SystemUI里面大量的模块都使用了此注入方式,比如StatusBar,QS,Keyguard等。
下面以 Android 11 源码的SystemUI来详细说明。

定义清单列表

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



    ...

    

    

    

    ...


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


AppComponentFactory

启动Application

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

  1. 调用super得到Application实例之后向其注册Context准备完毕的回调,该回调会执行SystemUIFactory和各组件的初始化:
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;
    }
    ...
}
  1. 在SystemUIApplication onCreate时执行上述回调, 构建SystemUIFactory,并对它进行初始化:
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();
        ...
    }
}
  1. 创建SystemUIFactory实例,并初始化SystemUI 的Dagger组件,并向Dependency实例注入依赖:
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();
    }
    ...
}
  1. Dependency里掌管着各式各样的依赖,被依赖的各实例通过Map管理。每个依赖都通过LazyDependencyCreator将各实例对应的懒加载回调缓存进去,然后保存在Map mProviders里。其后在各实例真正使用时调用createDependency() 进行创建:
public class Dependency {
    // 使用class作为key将对应实例缓存
    private final ArrayMap mDependencies = new ArrayMap<>();
    // 同样的class的key缓存实例的懒加载回调
    private final ArrayMap 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);
        ...
    }

    // 根据class查询 mDependencies里的缓存
    private synchronized  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 createDependency(Object cls) {
        Preconditions.checkArgument(cls instanceof DependencyKey || cls instanceof Class);
        LazyDependencyCreator 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 createDependency();
    }
}

启动SystemUIService

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

public class SystemUIService extends Service {
    ...
    @Override
    public void onCreate() {
        super.onCreate();

        // Start all of SystemUI
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        ...
    }
    ...
}

启动各类服务组件

回到 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 在上一节中有提到,下面讲解一下它的注入过程。
在 SystemUIRootComponent 中定义了 ContextComponentHelper 单例对象的获取方法,

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

上文dagger2注解基础里有提到,无参有返回值的方法,实例从定义的Module中查找,这里在 SystemUIModule 里可以找到,实际注入的是 ContextComponentResolver 对象,它是 ContextComponentHelper 的实现类:

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

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

@Singleton
public class ContextComponentResolver implements ContextComponentHelper {
    private final Map, Provider> mActivityCreators;
    private final Map, Provider> mServiceCreators;
    private final Map, Provider> mSystemUICreators;
    private final Map, Provider> mRecentsCreators;
    private final Map, Provider> mBroadcastReceiverCreators;

    @Inject
    ContextComponentResolver(Map, Provider> activityCreators,
            Map, Provider> serviceCreators,
            Map, Provider> systemUICreators,
            Map, Provider> recentsCreators,
            Map, Provider> 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 resolve(String className, Map, Provider> creators) {
        try {
            Class clazz = Class.forName(className);
            Provider 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, Provider>,即把上面的 systemUICreators 给注入进去了。

/**
 * SystemUI objects that are injectable should go here.
 */
@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);
    ...
}

以 bindsStatusBar 为例,参数 StatusBar 的实例又是由 StatusBarPhoneModule 提供的

/**
 * Dagger Module providing {@link StatusBar}.
 */
@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 里面定义的组件的实例对象注入过程就结束了。

总结

由于 SystemUI 模块内大部分结构都已经用上了 Dagger2 框架,本文旨在通过对 Dagger2 框架进行一些基础概念的总结,结合 SystemUI 部分的源码来了解其工作原理,这样在后续解读代码以及添加需求功能的时候可以用上。

你可能感兴趣的:(SystemUI 与 Dagger2 框架)