前言
在Android
开发中,控件绑定是一个久远的话题。
最开始就是使用findViewById
,满屏都是各种find;
后来出现了Butterknife
,使用注解来进行控件绑定,这样一来使UI层的代码清爽了很多,即使这样,还存在众多臃肿的全局变量的控件;
后来kotlin
推出了kotlin-android-e源码中的图片xtensiohttp代理ns 插件,可以直接用id就能得到xml
中的控件对象并且使用,这种方式真的很香,它原理是利用了字节码插桩技java环境变量配置术,帮我们自动生成了类似findViewById
的东西,这里可以参考一下郭神的博客:
❝
kotlin-android-extensions插件也被废弃了?扶我起来
blog.csdn.net/guolin_blog…
❞
具体废弃的原因我猜测:
1.不兼容Javajava培训
。虽然现在google
各种新技术都在以java
为主,像协程,Compose
之类,但是这些都是可以独立于平台的,而控件绑定这个功能是基于平台的,必然需要考虑jav源码编辑器下载a
用户群体。
2.虽然我们kotlin-android-extensions
我们使用起来非常爽,但是从它的实现原理也暴露出来一些问题,在无形之中降低了程序httpclient的运行效率。
使用
VieKotlinwBinding
的简单使用可以说是非常简单。
首先在我kotlin语言们的moudle
的build.gradle
下进行配置:
buildFeatures{ viewBindingtrue }
viewbinding
的配置是独立于moudle
的。
配置之后,会生成对应的Binding类,我们直接调用,进行绑定即可。
lateinitvarbinding:MainActivityBinding overridefunonCreate(savedInstanceState:Bundle?){ super.onCreate(savedInstanceState) binding=MainActivityBinding.inflate(layoutInflater) setContentView(binding.root) binding.message.text="Android开发那点事儿" }
在使用上,我们就可以直接通过binding
来获取到我们XML
布局中定义的控件,非常方便。
原理
生成的binding
文件 是在 build/generajava培训ted/data_binding_base_class_source_out/debug/out
目录下,我们先看下生成的类内容
这是我定义的XML布局:
<?xmlversion="1.0"encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MainFragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
这是生成的binding
类:
publicfinalclassMainActivityBindingimplementsViewBinding{ privatefinalConstraintLayoutrootView; publicfinalConstraintLayoutmain; publicfinalTextViewmessage; privateMainActivityBinding(ConstraintLayoutrootView,ConstraintLayoutmain,TextViewmessage){ this.rootView=rootView; this.main=main; this.message=message; } @Override @NonNull publicConstraintLayoutgetRoot(){ returnrootView; } @NonNull publicstaticMainActivityBindinginflate(@NonNullLayoutInflaterinflater){ returninflate(inflater,null,false); } @NonNull publicstaticMainActivityBindinginflate(@NonNullLayoutInflaterinflater, @NullableViewGroupparent,booleanattachToParent){ Viewroot=inflater.inflate(R.layout.main_activity,parent,false); if(attachToParent){ parent.addView(root); } returnbind(root); } @NonNull publicstaticMainActivityBindingbind(@NonNullViewrootView){ intid; missingId:{ ConstraintLayoutmain=(ConstraintLayout)rootView; id=R.id.message; TextViewmessage=ViewBindings.findChildViewById(rootView,id); if(message==null){ breakmissingId; } returnnewMainActivityBinding((ConstraintLayout)rootView,main,message); } StringmissingId=rootView.getResources().getResourceName(id); thrownewNullPointerException("MissingrequiredviewwithID:".concat(missingId)); } }
在这里viewbinding
帮我解析的xkotlin语言ml
布局文件,并对设置Id
的控件自动进行控件绑定,最后我们最后通过viewbinding
获取根布局,调用sehttp 404tContent
方法来添加布局。
@Override publicvoidsetContentView(Viewv){ ensureSubDecor(); ViewGroupcontentParent=mSubDecor.findViewById(android.R.id.content); contentParent.removeAllViews(); contentParent.addView(v); mAppCompatWindowCallback.getWrapped().onContentChanged(); }
原来是由actiivty
进行布局http 500解析,由我们自己进行控件绑定并使用,现在就相当于Viewgradle是什么意思Binding
将这两件事情都做了。
那这些binding
类是如何生成的呢?这就是接下来我们要探索的话题。
当我改变布局文件的时候,发现binding
类文件并不会实时发生改变,需要编译之后binding
类文件才会进行对应改变,由此推断,binding
类文件应该是APG在编译项目的时候生成的。
我们运行一下源码资本,看下Task
:

imagradle怎么读ge-202204051429342java环境变量配置87
整个编译流程中,只捕捉到了三个关于dataBinding
的task
,我们现在并不能确定是哪个task
生成的bgradleinding
类,那怎么办?
那我们就一个一个执行,一个一个去试。
在AS
右侧的gradle
任务栏中,找到了关于databinding
的task

ihttps和http的区别mage-20220405143309503
我们再分别执行 dajava编译器taBinkotlin面试题dingMergeDependencyArtifactsDebug
,dataBindingMergeGenClassesDebug
和 dataBindingGenBaseClassesDebug
。
先clean
操作,然后执行 前两个t源码时代ask
之后,发现只是生成了两个空文件夹,并未有内容生成:

image-20220405143551157
在执行了dataBHTTPindingMergeGenClassesDebug
之后,生成了我们所需要的binding类,

image-20220405143733504
那dataBindingMergeGenClassesDebug
这个tagradle教程sk
就是我们要探索的重点。
接下来我们引入AGP
源码和Databinding
源码,进行分析:
implementation'com.android.tools.build:gradle:7.0.2' implementation'androidx.databinding:databinding-compiler-common:7.0.2' implementation'androidx.databinding:databinding-common:7.0.2' implementation'com.android.databinding:baseLibrary:7.0.2'
在app
的build.gradle
中引入就行,我们在External Libraries
中进行查阅。
我们查找的入口主要有两个,一个AGP
的 TaskManager
类,这个是管理Task
创建的类,还有另外一个入口,就是从 /com/android/build/gradle/internal/tasks
路径去找,这个是属于熟能生巧的一个捷径。
首先看TaskManager
,通过方法查阅,我们会发现关于创建DataBinding Task
的只要一个方法 createDataBinjava模拟器dingTjava面试题asksIfNecessary
,
protectedfuncreateDataBindingTasksIfNecessary(creationConfig:ComponentCreationConfig){ valdataBindingEnabled=creationConfig.buildFeatures.dataBinding valviewBindingEnabled=creationConfig.buildFeatures.viewBinding if(!dataBindingEnabled&&!viewBindingEnabled){ return } taskFactory.register(DataBindingMergeBaseClassLogTask.CreationAction(creationConfig)) taskFactory.register( DataBindingMergeDependencyArtifactsTask.CreationAction(creationConfig)) DataBindingBuilder.setDebugLogEnabled(logger.isDebugEnabled) taskFactory.register(DataBindingGenBaseClassesTask.CreationAction(creationConfig)) //DATA_BINDING_TRIGGERartifactiscreatedfordatabindingonly(notviewbinding) if(dataBindingEnabled){ if(projectOptions[BooleanOption.NON_TRANSITIVE_R_CLASS] &&isKotlinKaptPluginApplied(project)){ //TODO(183423660):UndothisworkaroundforKAPTresolvingfilesatcompiletime taskFactory.register(MergeRFilesForDataBindingTask.CreationAction(creationConfig)) } taskFactory.register(DataBindingTriggerTask.CreationAction(creationConfig)) setDataBindingAnnotationProcessorParams(creationConfig) } }
这里首先获Kotlin取配置信息,看看ViewBinding
和 DataBindingkotlin语言
的开关状态,如果两个都是关闭直接返回。否则 进行Task
注册,在这里进行注册的task
,有两个是我们 在编译过程中看到的task
,再继续,就是当databkotlin和javainding
开启的时候,gradle是什么会再额外 注册task
,通过这里我们可以了解源码编辑器下载到 viewBindin源码编辑器g
只是DataBinding
中的一部分功能。viewbinding
只是进行控件绑定,DataBinding
除了基础的控件绑定之外,还拥有双向数据绑定等功能。
接下来我们看看DataBindingGenBaseClassesTask
。
这个Task
类的路径是 co源码时代m.android.build.gradle.internal.tasks.databindiJavang
,跟我说提到的第二个入口吻合,所以以HTTP后分析AGP
源码,可以gradle是什么从这个路径来找对应的Task
,这是一种取巧的方式。
写过自定义插件的朋友都知道,自定义Task
中 需要用注解 @TaskAction
来标识一下task
的运行入口。
@TaskAction funwriteBaseClasses(inputs:IncrementalTaskInputs){ recordTaskAction(analyticsService.get()){ valargs=buildInputArgs(inputs) CodeGenerator( args, sourceOutFolder.get().asFile, Logger.getLogger(DataBindingGenBaseClassesTask::class.java), encodeErrors, collectResources()).run() } }
可以看到 writeBaseClasses
方法被 @TaskAction
注解标识,那么这就是我们分析的入口。
这里主要是创建了 CodeGenerator
类,然后执行了 run()
方法。
overridefunrun(){ try{ initLogger() BaseDataBinder( LayoutInfoInput(args), if(symbolTables!=null)this::getRPackageelsenull) .generateAll(DataBindingBuilder.GradleFileWriter(sourceOutFolder.absolutePath)) }finally{ clearLogger() } }
在Codegradle是什么Generator:: run()
中,我们看到这里又创建了BaseDataBinder
类,并源码资本运行了 generateAll源码编辑器
方法。
fungenerateAll(writer:JavaFileWriter){ .............. layoutBindings.forEach{layoutName,variations-> ........... if(variations.first().isBindingData){ check(input.args.enableDataBinding){ "Databindingisnotenabledbutfounddatabindinglayouts:$variations" } valbinderWriter=BaseLayoutBinderWriter(layoutModel,libTypes) javaFile=binderWriter.write() classInfo=binderWriter.generateClassInfo() }else{ check(input.args.enableViewBinding){ "Viewbindingisnotenabledbutfoundnon-databindinglayouts:$variations" } valviewBinder=layoutModel.toViewBinder() javaFile=viewBinder.toJavaFile(useLegacyAnnotations=!useAndroidX) classInfo=viewBinder.generatedClassInfo() } ................. }
这里对代码进行了精简,这里首先源码中的图片做了一个判断,判断是否为DataBinding
,很明显我们需http://192.168.1.1登录要分析的内容 在else
里面。
在else
里面,先判断了viewBingradle安装与配置ding
是否开启,然后将java怎么读 BaseLayoutModel
对象转化为了 ViewBinder
对象,Java接下来执行了 ViewBinder
的
拓Java展方法 toJavaFile
,这个方法名的意思就很java面试题明显了,是去转化为Java
文件的。
funViewBinder.toJavaFile(useLegacyAnnotations:Boolean=false)= JavaFileGenerator(this,useLegacyAnnotations).create()
这里是创建了JavaFileGjava培训enerator
类 ,执行create()
方法。
funcreate()=javaFile(binder.generatedTypeName.packageName(),typeSpec()){ addFileComment("Generatedbyviewbindercompiler.Donotedit!") }
这里这就是创建bindinggradle教程
类的方法了 ,我们主要看下 typeSpec()
方法:
privatefuntypeSpec()=classSpec(binder.generatedTypeName){ 增加publicfinal修饰 addModifiers(PUBLIC,FINAL) 实现ViewBinding接口 addSuperinterface(ClassName.get(viewBindingPackage,"ViewBinding")) 添加rootView变量 addField(rootViewField()) 添加控件变量 addFields(bindingFields()) 创建无参构造方法 addMethod(constructor()) 创建根布局的get方法 addMethod(rootViewGetter()) if(binder.rootNodeisRootNode.Merge){ addMethod(mergeInflate()) }else{ 创建一个参数的inflate方法 addMethod(oneParamInflate()) 创建三个参数的inflate方法 addMethod(threeParamInflate()) } 添加bind方法 addMethod(bind()) }
使用过javapoet
的同学可以看出 这就是使用javapoet
来创建java
文件。
经过 这些创建流程 与我们生成的viewbinding
类文件对比,可以发现完全吻合。
所以我们的ViewBinding
类文件 就是在这里通过javapoet
生成的。
总结
我们最后总结一下:
我们通过观察编httpwatch译流程,得出dataBindingGenBaseClassesDebug
是生成binding
类的task
,然后通过TaskManager
找到对应的
DataBindingGenBaseClassesTask
,通过@TaskAction
注解找到task
执行的入口,最后调用到 DataBindkotlin和javaing
里面 BaseDataBindhttpwatcher
类,在这个过程中,通过 ViewBinder
调用到了 Jhttp://www.baidu.comavaFileGenerator
的 create() 方法,在这里通过jjava面试题avapoet
生成了我们所使用Viewbgradle是什么indihttp 404ng
类。
整体调用流程:
TaskManager ->writeBaseClasses ->CodeGenerator::run() ->BaseDataBinder::generateAll() ->ViewBinder::toJavaFile() ->JavaFileGenerator::create() ->typeSpec() ->javapoet
写在最后
整体流程并不是算复杂,大家在阅读后最好还java编译器是自己去跟一遍源码,这个亲自跟一遍,自己理解的才算透彻。
理论配合实践,才能真正学会。
评论(0)