dagger2源码解析(dagger2使用)
本文目录一览:
- 1、Dagger2使用与分析(7)---Lazy和Provider
- 2、Android11 Notification功能解析
- 3、Android--Dagger2入门
- 4、Dagger2入门(一)—— Dagger2优点
- 5、dagger2从入门到放弃-dagger.android
- 6、Dagger2学习笔记5(关于Lazy,Provide的使用)
Dagger2使用与分析(7)---Lazy和Provider
前言:Lazy和Provide可以用来包装我们需要注入的依赖,
1.Lazy为懒加载模式, 用到的时候才会依赖, 加载过一次之后就会只使用之前的实例,
2.而Provide为强制加载, 每次调用都会强制重新加载.
还是原来的套路,我们创建依赖类,这次为了对比普通注入,lazy和provider模式,我们分别创建3个实体类
如下:
public class Person {
}
public class Animal {
}
public class SuperMan {
}
创建自定义module
自定义Component
测试使用1:
查看控制台打印结果:
结论:可以看到普通注入只要声明就会被初始化, 而使用Provider和Lazy包装的并没有进行初始化
测试2:
下来我们分别对这些不同对象调用两次:
测试结果如下:
结论:以看到使用Provider包装的Animal, 每次调用都会重新获取新的实例, 而使用普通注入和使用Lazy包装都使用的是用一个实例, 从而也就能看出各自的区别.
注意:
我们看到, 当限定局部单例(@Scope或@Singleton)之后, 无论是Provider还是Lazy, 在同一个activity中只会获取同一个依赖对象.
Android11 Notification功能解析
我们知道,当手机有通知时,下拉通知中心中会显示所有的通知,该功能是在SystemUI中实现的,接着上篇文章 Android11 SystemUI解析 ,本文对通知相关的功能逻辑进行分析,基于Android11 CarSystemUI的通知功能逻辑展开分析。
关于通知功能逻辑,简单来说,无非就是四步,注册、发送、接收、显示,那么接下来针对以上四步进行源码详细分析。
关于CarSystemUI启动及相关逻辑可以参考文章 Android11 SystemUI解析 ,本文就不赘述了,直接以NotificationPanelViewController类为入口进行分析:
从构造方法来看:
可以看到,在创建以上实例时,会通过Inject的方式来创造对应参数的实例,该功能是通过dagger2来实现,具体对应的Module为CarNotificationModule类,看一下CarNotificationListener实例创造时的实现,关于NotificationViewController后面再分析:
可以看到,在provideCarNotificationListener()提供CarNotificationListener实例时,还执行了registerAsSystemService()方法,接下来看一下CarNotificationListener类:
CarNotificationListener继承了NotificationListenerService类,该类继承了Service,是framework内部的组成部分,通过registerAsSystemService()来看一下该类的实现:
该方法内部主要执行了两项操作:
a.创建了NotificationListenerWrapper对象,该类继承了INotificationListener.Stub,主要用来接收回调,后面在显示环节进行详细分析;
b.将以上对象作为参数通过INotificationManager的registerListener进行注册;
通过getNotificationInterface()的是实现可以知道,registerListener()执行到了NotificationManagerService里面去了,接下来一起看一下:
mListeners是NotificationListeners实例,是在init()中进行初始化的,NotificationListeners是其内部类,看一下具体实现:
NotificationListeners继承了ManagedServices,registerSystemService方法是在ManagedServices里面实现的,看一下:
根据调用关系,registerServiceImpl()方法内先将前面创建的INotificationListener(mWrapper)作为参数创建了ManagedServiceInfo实例info,然后执行linkToDeath进行死亡监测,最后将info加入mServices中进行管理,执行完后再执行onServiceAdded(info),该方法是在NotificationListeners类内部实现的,再回去看一下该方法,最终会调用到CarNotificationListener.java里面的onListenerConnected()方法。
以上注册过程逻辑比较绕,用一张图来总结一下:
发送过程比较简单,按照系统提供的方法来发送即可,主要涉及NotificationChannel、Notification、NotificationManager这三个类,简单看一下:
首先某个应用在发送通知前需要创建该应用对应的NotificationChannel,然后在通知中传入对应channel ID就可以了,创建如下:
在创建NotificationChannel时需要传入唯一的id、name和importance,创建如下:
创建完NotificationChannel后,再创建Notification,Notification创建采用的是Builder模式,主要涉及的内容比较多,创建如下:
Notification涉及的内容比较多,可以根据需要进行设定;
创建完Notification后,通过NotificationManger来进行发送就可以了:
执行完notify后续的逻辑处理过程,在接收环节进行分析;
发送时会调用到notify()方法:
跟随调用关系,会调用到notifyAsUser()方法:
在notifyAsUser()会调用到NotificationManagerService中的enqueueNotificationWithTag()方法,先看一下fixNotification()方法:
需要注意一下:当应用targetSdkVersion大于22时,在创建Notification时需要传入smallIcon,否则会抛异常导致发送不成功;接下来看一下enqueueNotificationWithTag()方法:
NotificationManagerService继承了SystemService,在SystemServer中会进行启动,在start()方法内部会执行publishBinderService来进行Binder注册提供服务:
可以看到,enqueueNotificationWithTag()会调用到enqueueNotificationInternal(),该方法是真正的逻辑实现:
该方法中主要做了以下几件事:
1.进行各种check来确保notification的合法性;
2.将Notification作为参数创建StatusBarNotification;
3.获取Notification对应的channel id,根据channel id 来获取应用对应的NotificationChannel,如果为空的话,就直接返回了,因此应用在发送notification前需要先创建对应NotificationChannel;
4.通过Handler post EnqueueNotificationRunnable来执行后续逻辑;
在EnqueueNotificationRunnable内部会将r(NotificationRecord)加入mEnqueuedNotifications进行管理,然后判断该NotificationRecord是否已经存在过,最后执行PostNotificationRunnable;
PostNotificationRunnable的run()中主要处理逻辑如下:
1.从mEnqueuedNotifications中找到跟key对应的NotificationRecord;
2.通过indexOfNotificationLocked()看mNotificationList里面是否已经包含该NotificationRecord,如果不存在,说明是新的record,需要加入mNotificationList进行管理;否则的话,将mNotificationList中index对应的NotificationRecord进行更新;
3.将要处理的NotificationRecord放入mNotificationsByKey进行管理;
4.执行mListeners.notifyPostedLocked(r, old)来进行通知;
5.在finally里面将要处理的NotificationRecord从mEnqueuedNotifications里面移除;
6.如果在加入后有取消操作,需要立刻执行取消操作,并将NotificationRecord从mDelayedCancelations中移除;
前面讲到,在PostNotificationRunnable中会执行mListeners.notifyPostedLocked(r, old)进行通知,mListeners是NotificationListeners实例:
跟随调用关系:
通过getServices()来找到已经注册过的ManagedServiceInfo列表,最后执行notifyPosted();
在notifyPosted()内部通过info.service来找到对应的INotificationListener实例,对应NotificationListenerService内部的NotificationListenerWrapper,然后将StatusBarNotification封装成StatusBarNotificationHolder,最后执行NotificationListenerWrapper的onNotificationPosted()方法:
最终会通过Handler来发送消息来进行处理;
onNotificationPosted(sbn, rankingMap)是在CarNotificationListener内部实现的;
在CarNotificationListener内部会将StatusBarNotification封装成AlertEntry,然后执行notifyNotificationPosted():
一步一步调用:
先将alertEntry存入mActiveNotifications进行管理;然后执行sendNotificationEventToHandler发送NOTIFY_NOTIFICATION_POSTED消息;
该Handler是通过setHandler来赋值的,具体是在什么地方呢?
这个需要回到最前面NotificationPanelViewController里面了,前面说到NotificationViewController是在显示环节进行分析,轮到NotificationViewController登场了;
NotificationViewController是在NotificationPanelViewController实例化,并执行enable()方法,先看一下构造方法:
在构造方法内部,会传入CarNotificationView、CarNotificationListener、PreprocessingManager等实例,都是跟显示有关的核心类;
1.CarNotificationView:负责处理通知显示;
2.PreprocessingManager:负责管理通知,通过CarNotificationListener来获取通知;
3.CarNotificationListener:跟NotificationManagerService间接交互的类;
可以看到Handler是在NotificationViewController里面实现的,当有消息到来时,如果CarNotificationView显示时执行updateNotifications()来直接显示通知;不显示时执行resetNotifications()来对通知进行管理;
以上就是Notification的整个工作过程,最后用一张流程图来总结一下:
Android--Dagger2入门
dagger拥有两种方式注入对象,一种是利用 @Inject注解 构造函数
在moudle的gradle中做如下配置,我在2.30.1版本中尝试使用构造函数注入,发现怎么编译都会报错,结果最后使用最新版本就可以了:
这边定义两个类,分别代表本地和远程的数据源,并对构造函数使用@Inject注解
定义包装类DataSource,包含上面两个类,同样对构造函数使用@Inject注解
对于需要注入的对象,dagger并不是直接注入,而是需要一个中间件去注入他们,使用代理模式的思想,这样的好处是方便管理和控制
build下项目后,dagger会对应生成一个DaggerApplicationComponent类,通过上篇文章,我们知道用的是APT技术
调用生成类DaggerApplicationComponent的注入方法
运行后日志:
2021-12-04 /com.aruba.daggerapplication I/aruba_log: DataSource(remoteDataSource=com.aruba.daggerapplication.di.datasource.RemoteDataSource@1ae5a6b, localDataSource=com.aruba.daggerapplication.di.datasource.LocalDataSource@8b49c8)
可以看到dagger帮我们自动生成注入对象了,并且我们在使用的地方不需要关注它是如何生成的
第二种方式就是模块注入,构造函数注入是以类为对象,模块注入则是以方法为对象
接下来尝试使用网络请求,以获取百度的首页HTML
别忘了在Manifest.xml中添加权限
效果:
模块注入同样也实现了自动注入对象,并且这种方式可读性和可维护性更高
通过上面两种方式,我们知道了如何注入对象,但是我们并不知道注入的对象的生命周期,有时我们希望获取的对象是一个单例,这种情况仅仅使用注入是无法实现的
下面例子,通过注入两个相同类型对象,查看它们是否是同一份实例
在MainActivity中同时注入两个DataSource对象,并通过打印日志,观测结果
日志打印:
2021-12-04/com.aruba.daggerapplication I/aruba_log: DataSource(remoteDataSource=com.aruba.daggerapplication.di.datasource.RemoteDataSource@137fdbe, localDataSource=com.aruba.daggerapplication.di.datasource.LocalDataSource@31b1e1f)
2021-12-04/com.aruba.daggerapplication I/aruba_log: DataSource(remoteDataSource=com.aruba.daggerapplication.di.datasource.RemoteDataSource@b3756c, localDataSource=com.aruba.daggerapplication.di.datasource.LocalDataSource@7b81735)
2021-12-04/com.aruba.daggerapplication I/aruba_log: false
结果显示这两个对象不是同一个实例
在使用构造注入或Module注入时,一旦使用了作用域注解,其Component也要使用相同的作用域注解,否则编译会报错。同一个Component实例在注入对象时,一旦发现注入方式使用了作用域,那么它们注入的对象将会是同一份实例
@Singleton注解为dagger默认提供的一个作用域注解。定义一个构造函数注入方式,并使用该注解
在Component中,使用相同作用域,并且我重新定义了一个ScopeActivity来测试结果
ScopeActivity中需要注入两个SingletonTest 对象,并打印是否为同一个实例
日志结果:
2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton1 hashcode: 246939604
2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton2 hashcode: 246939604
结果显示,这两个对象是同一份实例
将上面的作用域注解替换成MyScope
日志结果:
2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton1 hashcode: 246939604
2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton2 hashcode: 246939604
和使用@Singleton是相同的效果
模块注入方式,使用作用域注解在方法上:
即使用了同一个作用域,不同的Component实例进行注入,最后生成的对象还是不同的实例,即作用域管理的生命周期是跟随Component的。但一般情况下,我们一个APP只需要一份Component实例,而一个App中,往往有着不同的作用域
在MainActivity中,也定义注入一个SingleTest对象,注意每调用一次DaggerApplicationComponent.create(),会创新一个新的DaggerApplicationComponent对象
跳转到ScopeActivity后,我查看下打印结果:
2021-12-04/com.aruba.daggerapplication I/aruba_log: MainActivity singleton hashcode: 20446654
2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton1 hashcode: 127836367
2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton2 hashcode: 127836367
MainActivity和ScopeActivity中都调用了Component的create方法,所以两份Component实例注入的对象是不同的实例
如果想要一个Component下使用不同的作用域,Component是不支持的,但Subcomponent可以使用, Subcomponent又可以被添加到Component中
同时在子组件中提供注入方法,这边我新建了一个SubActivity
在Moudle注解中,指定使用哪个子组件
MainActivity中注入打印,并跳转到SubActivity
SubActivity中创建子组件,并注入
日志结果:
2021-12-04/com.aruba.daggerapplication I/aruba_log: MainActivity singleton hashcode: 257322958
2021-12-04/com.aruba.daggerapplication I/aruba_log: SubActivity singleton hashcode: 257322958
2021-12-04/com.aruba.daggerapplication I/aruba_log: SubActivity subObject1 hashcode: 219854405
2021-12-04/com.aruba.daggerapplication I/aruba_log: SubActivity subObject2 hashcode: 219854405
前两条证实了同一个父Component实例,就算使用子组件注入,作用域也有作用,后面两条说明我们成功的注入了子组件生成的对象
最后,附上一张dagger的结构图:
Dagger2入门(一)—— Dagger2优点
Dagger2入门(一)—— Dagger2优点
Dagger2入门(二)—— Dagger2的简单使用
Dagger2入门(三)—— Dagger2的高级用法
Dagger2使我们在开发中经常遇到的依赖注入框架。但是为什么要用dagger2呢?换句话说就是为什么用依赖注入的方式去创建对象呢?很多人的答复是为了解耦,但一般模式耦合在哪里?为什么就不能用new的方法呢?
这对于许多开发经验不是那么充足的猿来说是比较难理解的,在这里我建议新猿看到这里,不妨先放下Dagger2,先学习一下 工厂方法模式 、 享元模式 以及MVC,MVP,MVVM模式。
Dagger2本质上是帮你生成一个管理依赖对象的工厂。
当我们不想让一个类的创建与其使用的地方发生耦合,或者我们想要复用同一对象时,dagger2可以为我们自动为我们生成工厂方法并把生成的对象注入到我们需要的地方。这个优点在当一个工厂复杂到一定程度的时候比较明显,比如我们再多个界面上需要到User信息是,可以靠Dagger2保证是同一User。当然你可以说你可以自己写一个工厂方法,毕竟dagger2的原理就是自动帮你生成工厂。当这样的依赖多了的时候,恐怕你就不会这么说了。
当项目复杂达到一定程度,我们想要将Model层分离出来时,Dagger2优点更明显,Model层自己负责自己的创建。而Model的使用则通过依赖注入的方式交给P层,C层,VM层。
再假设,一个类A,依赖于类B,再依赖于类C,他的构建方法是这样的 new A(new B(new C())) 或者其他复杂的依赖关系。当我们要使用A的时候,如果使用传统方式就不可避免地与B、C发生了耦合,但如果采用依赖注入的方式,使用A的地方通过注入的方式获得A实例,A内部通过注入的方式获得B对象(依赖),B也同理,通过Dagger2达到类管理依赖也实现了 迪米特法则 。
dagger2从入门到放弃-dagger.android
上一章中介绍了Activities Subcomponents Multibinding的实现过程,但是多出那么多步骤还是让人有些头晕;而使用dagger.android可以简化这一过程
需要引入"com.google.dagger:dagger-android:${dagger_version}"
如果需要注入SupportLib中的Fragment还需要引入"com.google.dagger:dagger-android-support:${dagger_version}"
引入注解处理器"com.google.dagger:dagger-android-processor:${dagger_version}"
定义一个抽象的Module类,在里面定义一个返回对应Activity使用@ContributesAndroidInjector的抽象方法
dagger.android会根据@ContributesAndroidInjector生成需要注入对应对象的SubComponent,并添加到map中
这一步相当于完成了上篇文章中的step2、3、4、5,最终dagger.android会生成需要添加到map中的SubComponet.Builder
将包含@ContributesAndroidInjector注解方法的Module和dagger.android提供的Module安装到AppComponent中
在Application中实现HasActivityInjector接口
dagger.android还可以支持其他三大组件以及Fragment的注入,具体可以查看 DaggerApplication 的源码
在activity中完成注入
看看 AndroidInjection.inject(this) 到底干了啥
AndroidInjector就比较好理解了,它其实就是一个SubComponent的下层接口,而AndroidInjector.Factory就是它的Builder
大大简化了Multibindings的实现过程
dagger2从入门到放弃-概念
dagger2从入门到放弃-最基础的用法介绍
dagger2从入门到放弃-Component的继承体系、局部单例
dagger2从入门到放弃-ActivityMultibindings
dagger2从入门到放弃-dagger.android
dagger2从入门到放弃-其他用法
dagger2从入门到放弃-多模块项目下dagger的使用
dagger2从入门到放弃-为何放弃
Dagger2学习笔记5(关于Lazy,Provide的使用)
Dagger2学习笔记1(基础概念学习)
Dagger2学习笔记2(学习Dagger2的简单使用)
Dagger2学习笔记3(各个注解学习)
Dagger2学习笔记4(@Singleton 与@ Scope 实现全局单例与作用域单例)
之前的学习中我们了解了Dagger2的大部分使用方法, 接下来学习Lazy, Provide的使用.
Lazy和Provide可以用来包装我们需要注入的依赖, Lazy为懒加载模式, 用到的时候才会依赖, 加载过一次之后就会只使用之前的实例, 而Provide为强制加载, 每次调用都会强制重新加载.
定义一个可以提供flower, dog和car的module, 都没有使用singleton或者scope注解
先什么都不做, 只是声明注入对象, 分别使用普通注入, Provider包装, Lazy包装. 控制台输出结果:
可以看到普通注入只要声明就会被初始化, 而使用Provider和Lazy包装的并没有进行初始化, 接下来我们分别对这些不同对象调用两次:
控制台输出:
可以看到使用Provider包装的dog, 每次调用都会重新获取新的实例, 而使用普通注入和使用Lazy包装都使用的是用一个实例, 从而也就能看出各自的区别.
以上为依赖没有使用Singlton或者Scope限定单例范围, 接下来我们限定单例, 看看结果如何, 修改Module文件:
在运行得到结果:
我们看到, 当限定局部单例之后, 无论是Provider还是Lazy, 在同一个activity中只会获取同一个依赖对象.
我们已经学习了Dagger的各种使用, 接下来会对Dagger的实现原理进行学习!
End~