Dagger2和它在SystemUI上的应用

本文详细介绍了Dagger2在AndroidSystemUI中的应用,包括如何通过AppComponentFactory进行组件初始化,以及Dependency类如何管理大量系统组件的依赖注入。文章强调了Dagger2在大型项目中的优势和其在SystemUI复杂组件间的高效管理作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

private val localData: MovieLocalData,
private val remoteData: MovieRemoteData
)

class MovieLocalData @Inject constructor()

@Singleton
class MovieRepository @Inject constructor(
private val localData: MovieLocalData,
private val remoteData: MovieRemoteData
)

6. 添加Network模块

@Module
class NetworkModule {

@Singleton
@Provides
fun provideLoginService(okHttpClient: OkHttpClient, gsonConverterFactory: GsonConverterFactory): MovieService {
return Retrofit.Builder()
.baseUrl(“http://omdbapi.com/”)
.addConverterFactory(gsonConverterFactory)
.client(okHttpClient)
.build()
.create(MovieService::class.java)
}
}

依赖关系图

Dagger2的功能十分强大,上述实战仍有不少未提及的高阶用法,感兴趣者可进一步尝试。

  • 自定义作用域的@Scope
  • 注释子组件的@Subcomponent
  • 注释抽象方法的@Binds
  • 一个接口指定多个实现的@Named

Dagger2导航的支持

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

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

Dagger2在SystemUI上应用

对于小型项目而言,引入DI框架显得大材小用、大动干戈。而且对于后期接手人员,如果对于DI框架不熟悉的话,维护将变得尤为困难。似乎只有大型项目才能让它自由地施展拳脚。

前些年我在调查某个导航栏Bug的时候查阅过SystemUI的代码,当时意外地发现大量的模块包括StatusBar、Recents、Keyguard等都是DI方式引入的。虽然对Dagger略有耳闻,但仍看得云里雾里,不得其解。

SystemUI作为Android系统里最核心最复杂的App,称之为大型项目毫不过分。现在就来看看Dagger2如何助力这个大型App管理大量的系统组件。

※ 源码版本:Android 11

SystemUI中主要的依赖实例都管理在Denpency类中。

public class Dependency {

@Inject @Background Lazy mBackgroundExecutor;
@Inject Lazy mClockManager;
@Inject Lazy mActivityManagerWrapper;
@Inject Lazy mDevicePolicyManagerWrapper;
@Inject Lazy mPackageManagerWrapper;
@Inject Lazy mSensorPrivacyController;
@Inject Lazy mDockManager;
@Inject Lazy mINotificationManager;
@Inject Lazy mSysUiStateFlagsContainer;
@Inject Lazy mAlarmManager;
@Inject Lazy mKeyguardSecurityModel;
@Inject Lazy mDozeParameters;
@Inject Lazy mWallpaperManager;
@Inject Lazy mCommandQueue;
@Inject Lazy mRecents;
@Inject Lazy mStatusBar;
@Inject Lazy mDisplayController;
@Inject Lazy mSystemWindows;
}

后面以StatusBar实例的注入为例阐述下SystemUI里Dagger2的注入流程。

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

// AndroidManifest.xml
<application
android:name=“.SystemUIApplication”

tools:replace=“android:appComponentFactory”
android:appComponentFactory=“.SystemUIAppComponentFactory”>

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

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);
SystemUIFactory.getInstance().getRootComponent().inject(
SystemUIAppComponentFactory.this);
}
);
}

return app;
}

}

Application的onCreate()回调的时候意味着Context已准备完毕,接着执行上述回调。

public class SystemUIApplication extends Application implements
SystemUIAppComponentFactory.ContextInitializer {

@Override
public void setContextAvailableCallback(
SystemUIAppComponentFactory.ContextAvailableCallback callback) {
mContextAvailableCallback = callback;
}

@Override
public void onCreate() {

log.traceBegin(“DependencyInjection”);
mContextAvailableCallback.onContextAvailable(this);★
mRootComponent = SystemUIFactory.getInstance().getRootComponent();
mComponentHelper = mRootComponent.getContextComponentHelper();

}
}

回调将先创建SystemUIFactory实例,并初始化SystemUI App的Dagger组件。之后初始化DI子组件并向Dependency实例注入依赖。

public class SystemUIFactory {
public static void createFromConfig(Context context) {

try {
Class<?> cls = null;
cls = context.getClassLoader().loadClass(clsName);
// 1. 创建SystemUIFactory实例
mFactory = (SystemUIFactory) cls.newInstance();
mFactory.init(context);
}
}

private void init(Context context) {
// 2. 取得SystemUI的Dagger组件实例
mRootComponent = buildSystemUIRootComponent(context);
// 3. 创建Dependency实例并绑定到DependencyInjector子组件中
Dependency dependency = new Dependency();
mRootComponent.createDependency().createSystemUI(dependency);
// 4. 初始化Dependency
dependency.start();
}

// 初始化Dagger组件
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerSystemUIRootComponent.builder()
.dependencyProvider(new DependencyProvider())
.contextHolder(new ContextHolder(context))
.build();
}

}

Dependency类里掌管着各式各样的依赖,被依赖的各实例通过Map管理。但并不是在初始化的时候就缓存它们。而先将各实例对应的懒加载回调缓存进去。其后在各实例确实需要使用的时候通过注入的懒加载获取和缓存。

public class Dependency {
// 使用class作为key将对应实例缓存的Map
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
// 缓存实例的懒加载回调的Map
private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();

protected void start() {
mProviders.put(ActivityStarter.class, mActivityStarter::get);
mProviders.put(Recents.class, mRecents::get);
mProviders.put(StatusBar.class, mStatusBar::get);
mProviders.put(NavigationBarController.class, mNavigationBarController::get);

}

// 根据class查询缓存,尚未缓存的话通过懒加载回调获取注入的实例并缓存
private synchronized T getDependencyInner(Object key) {
T obj = (T) mDependencies.get(key);
if (obj == null) {
obj = createDependency(key);
mDependencies.put(key, obj);
if (autoRegisterModulesForDump() && obj instanceof Dumpable) {
mDumpManager.registerDumpable(obj.getClass().getName(), (Dumpable) obj);
}
}
return obj;
}

protected T createDependency(Object cls) {
Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
LazyDependencyCreator provider = mProviders.get(cls);
return provider.createDependency();
}

private interface LazyDependencyCreator {
T createDependency();
}
}

Application创建好之后SystemUI的主Service将启动起来,并逐个启动其他Service。

public class SystemUIService extends Service {

@Override
public void onCreate() {
super.onCreate();
// Start all of SystemUI
((SystemUIApplication) getApplication()).startServicesIfNeeded();

}
}

通过ContextComponentHelper解析预设的service类名得到实例并启动。

public class SystemUIApplication {
public void startServicesIfNeeded() {
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 {
// 从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;
}

mServices[i].start();

}
mRootComponent.getInitController().executePostInitTasks();
}
}

配置的Service列表。

// config.xml


com.android.systemui.recents.Recents
com.android.systemui.volume.VolumeUI
com.android.systemui.stackdivider.Divider
com.android.systemui.statusbar.phone.StatusBar ★

ContextComponentHelper单例已声明由Dagger组件提供。

@Singleton
@Component(modules = {…})
public interface SystemUIRootComponent {

/**

  • Creates a ContextComponentHelper.
    */
    @Singleton
    ContextComponentHelper getContextComponentHelper();
    }

模块SystemUIModule负责注入ContextComponentHelper实例,实际注入的是ContextComponentResolver实例。

@Module(…)
public abstract class SystemUIModule {

/** */
@Binds
public abstract ContextComponentHelper bindComponentHelper(
ContextComponentResolver componentHelper);
}

ContextComponentResolver用于解析Activity和Service等实例,通过class实例从Map查询得到的Provider里取得对应的Service实例。 它的构造函数注释了@Inject。它依赖几个Map参数,比如StatusBar的Provider是注入到其中的SystemUI Map里。

@Singleton
public class ContextComponentResolver implements ContextComponentHelper {
@Inject
ContextComponentResolver(Map<Class<?>, Provider> activityCreators, Map

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值