引子(可忽略)
世界一词的来源,昨天突然想到了好像来源于佛教,于是特地google一番:一人一世界,大千世界。因为现代“世界”一词,导致我们对世界的理解过于狭隘。阿弥陀佛,罪过!!!
1.在AAC系列的GithubBrowserSample中有该案例案例中看到了dagger,按赖不住内心的好奇(第一次认真对待该源码,以前认为太麻烦,或者不想学,或者…),demo中的版本是2.16,目前已经被谷歌接收维护了,源码在此,注意了,你要是想看指定版本,例如我的是2.16,去2.16版本下载即可(下图送给和我一样“聪明”的人)
2.我也是看了很多天,从dagger2看到注解,从注解(原名叫控制反转,在200几年的时候被专家重新定义为注解,这里是真的专家,不服不行)看到反射,从反射有回到现在的dagger2。dagger源码解析,谷歌了N遍,都没有得到我要的答案:dagger2究竟是如何在build中生成对应的注解文件的,所有的回答都是一样的,哈,apt!!!还给我写了apt咋用,对我这么笨…咳咳,对我这么智慧的人给我
这些啥用,我不只是不会用,而且压根不知道dagger2中能complie(注解处理器,核心部分,如何生成build代码的)代码在哪好吧!!!过粪~
学习dagger2思路整理
1.网上找demo
这有一篇文章apt的使用:Android 利用 APT 技术在编译期生成代码
2.对照GithubBrowserSample中的用法,了解该项目中dagger2的实际使用;这里一篇文章有助于理解本demo:https://www.jianshu.com/p/f2977bf04c5e
3.对存在的疑问点自己谷歌(非常讨厌百度,但是没办法,被墙大就自己想办法);
4.最后源码学习;
以上是个人作为学习dagger2的一个思路,并非本章内容章节目录。
android引入dagger:
如下:这里build.gradle总共引入了5个dagger相关包:
build.gradle
//这个是com.google.dagger:dagger:$versions.dagger
implementation deps.dagger.runtime
//这个是com.google.dagger:dagger-android:$versions.dagger
implementation deps.dagger.android
//这个是com.google.dagger:dagger-android-support:$versions.dagger
implementation deps.dagger.android_support
...
//com.google.dagger:dagger-compiler:$versions.dagger
kapt deps.dagger.android_support_compiler
//com.google.dagger:dagger-android-processor:$versions.dagger
kapt deps.dagger.compiler
versions.gradle
def dagger = [:]
dagger.runtime = "com.google.dagger:dagger:$versions.dagger"
dagger.android = "com.google.dagger:dagger-android:$versions.dagger"
dagger.android_support = "com.google.dagger:dagger-android-support:$versions.dagger"
dagger.compiler = "com.google.dagger:dagger-compiler:$versions.dagger"
dagger.android_support_compiler = "com.google.dagger:dagger-android-processor:$versions.dagger"
deps.dagger = dagger
当我们项目构建后,如下图所示都是implementation 引入进来的,那么kapt的呢???
com.google.dagger:dagger-compiler在哪?com.google.dagger:dagger-android-processor:$versions.dagger在哪?这两个kapt(注解处理工具)呢?可能吧,我比较死板,我花费的功夫至少一半在这个上面了,网上到目前为止我也没找到谁说了这句话,我认为他们too yang too sample(大佬们:切,这么简单的问题不屑于写上去罢了!!!),还别说当满世界找不到shi,那都是最珍贵的,所以这个算本章 重点:
1.首先我们看com.google.dagger🗡2.16(另外两个implementation引入的雷同),代码如下:
我们再从github中提供的源码中也能找到对应的代码:
2.我们再看com.google.dagger:dagger-compiler和com.google.dagger:dagger-android-processor,我信你个鬼,毛都没有,我找了怀疑人生:最后。。。在generate_poms.py文件,该文件目录:dagger-dagger-2.16\util\maven,而且比较有意思,这个文件我看了最新的2.28.3版本没有,反正思路是这个,自己找。虽然python咱不熟,但是不妨碍我们去看,这里大致意思生成相应的文件夹目录,并且有别名。
来来来,咱验证下可对:我们去看下AppInjector文件中,有个DaggerAppComponent,该文件继承AppComponent,但是生成的“Dagger”+AppComponent在哪?
fun init(githubApp: GithubApp) {
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp)
githubApp
.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
handleActivity(activity)
}
。。。
}
文件的命名这里可以看出来见名知意,服!!!在\codegen文件夹中ComponentGenerator.java文件中,如下所示。问你服不服,不服吹的你一脸口水,再问你服不服以此do while操作,直到说服然后return;并继续下面的流程
static ClassName componentName(TypeElement componentDefinitionType) {
ClassName componentName = ClassName.get(componentDefinitionType);
return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
}
不得不说,大佬还是大佬,我在成为大佬的路上…没错,去找大佬,然后跪下…
包名,类,方法名,变量等的命名就可以看出一个人的水准,哎~~~
源码解析(终于到了只有痛苦,没有迷茫的时间段了)
声明:这篇根本不能算dagger的源码解析,真正涉及到源码部分我也只是按照自己的理解去随便提下,我理解的也非常模糊。感觉网上的真正说谷歌dagger源码的并没有,如果谁有,麻烦给我提供个地址,让我去认真学习一番,我说的是对照谷歌上dagger源码的解析。dagger源码涉及面(仅仅个人理解,因为我也不是很肯定,我相信后面肯定会深入去了解):python编程+gwt框架+grpc框架+javaoet(这个比较简单)+注解+android Framework等
一.dagger2如何为我们生成文件并放到build下
1.ComponentGenerator.java生成了Dagger+AppComponent(我们app中定义的文件):DaggerAppComponent.java
我们再看里面的局部代码片段(后面我相信我会详细写,这里我也并没有去系统理解,或者很多查找源码,自己就是根据它的命名找的,这里也体现了见名知意的好处),如下是DaggerAppComponent.java代码局部片段
@Override
public AppComponent build() {
if (appModule == null) {
this.appModule = new AppModule();
}
if (application == null) {
throw new IllegalStateException(Application.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
对应于dagger中在哪里创建的?可以理一下,
private MethodSpec buildMethod() {
MethodSpec.Builder buildMethod;
if (builderSpec().isPresent()) {
ExecutableElement specBuildMethod = builderSpec().get().buildMethod();
// Note: we don't use the specBuildMethod.getReturnType() as the return type
// because it might be a type variable. We make use of covariant returns to allow
// us to return the component type, which will always be valid.
buildMethod =
methodBuilder(specBuildMethod.getSimpleName().toString()).addAnnotation(Override.class);
} else {
buildMethod = methodBuilder("build");
}
buildMethod.returns(ClassName.get(graph.componentType())).addModifiers(PUBLIC);
builderFields.forEach(
(requirement, builderField) -> {
switch (requirement.nullPolicy(elements, types)) {
case NEW:
buildMethod.addCode(
"if ($1N == null) { this.$1N = new $2T(); }", builderField, builderField.type);
break;
case THROW:
buildMethod.addCode(
"if ($N == null) { throw new $T($T.class.getCanonicalName() + $S); }",
builderField,
IllegalStateException.class,
TypeNames.rawTypeName(builderField.type),
" must be set");
break;
case ALLOW:
break;
default:
throw new AssertionError(requirement);
}
});
buildMethod.addStatement("return new $T(this)", componentName);
return buildMethod.build();
}
首先在ComponentGenerator的write方法,该方法主要功能是编写DaggerXXComponent里面的具体方法,这里调用了ComponentWriter.writeComponent,
@Override
Optional<TypeSpec.Builder> write(ClassName componentName, BindingGraph input) {
return Optional.of(
ComponentWriter.writeComponent(
types, elements, keyFactory, compilerOptions, componentName, input));
}
这里我不全看,仅仅看ComponentBuilder.create,这里面的代码及完成DaggerXXComponent中Builder的代码生成
static TypeSpec.Builder writeComponent(
...
Optional<ComponentBuilder> builder =
ComponentBuilder.create(name, graph, subcomponentNames, elements, types);
...
}
2.GithubApp中定义了@Inject如下所示,首先声明一点该文件其实已经生了DispatchingAndroidInjector_Factory,因为这个注解是DispatchingAndroidInjector构造器中的注解,我只是想看看在那个类中生成的:SourceFiles.java的,实打实硬一个个文件找的!!!
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
这里声明一点,该dagger源码我没有在除了该代码自带注解以外的指引,所以只能一个个的搜,当然这里有些技巧,八仙过海各显神通了,我的技巧就是定位在codegen文件夹中,生成的代码如下,生成了xx_Factory文件:
/**
* Returns the generated factory or members injector name for a binding.
*/
static ClassName generatedClassNameForBinding(Binding binding) {
switch (binding.bindingType()) {
case PROVISION:
case PRODUCTION:
ContributionBinding contribution = (ContributionBinding) binding;
switch (contribution.kind()) {
case INJECTION:
case PROVISION:
case PRODUCTION:
return elementBasedClassName(
MoreElements.asExecutable(binding.bindingElement().get()), "Factory");