因为gradle已经出来好久了,相关配置文章也比较多,因此就不从头开始说了,这里推荐几篇文章:html
https://www.jianshu.com/p/8b8a550246bd
java
刚哥(任玉刚)亲笔:gradle系列文章(这里我想催更!)android
Android studio已经出来好久了,相信大部分公司都已经从eclipse转AS了,反正我都快忘记eclipse如何操做了。AS不少强大功能,其中很突出的一项就是gradle构建。还记得第一次用依赖的时候,那感受爽翻。可是由于build代码不熟悉,也遇到不少坑,常常会莫名其妙报错,当时只能上网查,而后一板一眼的配置。做为程序猿这种不能彻底掌握的感受是最不爽的,很早就想完全掌控它了。windows
其次,做为有梦想的咸鱼,不能只作代码的搬运工,这种高阶必备的知识点仍是须要掌握的。好比国内比较火热的插件化、热更新都会涉及到gradle插件知识,因此想要进阶,必须掌握gradle。
缓存
经过学习上文推荐文章,咱们已经了解到,gradle就是构建工具,他使用的语言是groovy,咱们能够在build.gradle里面写代码来控制,固然,若是代码不少,但愿单独提取出来,那么可使用自定义gradle插件来实现,没错,咱们的主角:AndroidDSL(plugin)就是一个自定义插件而已,因此学习它以前须要了解如何自定义gradle插件。
微信
首先,咱们新建一个项目,会获得两个build.gradle,一个是主项目的,一个是全局的。咱们先只看项目里的build文件,初始状态以下:
多线程
apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.xtu.neo.mylibrary" minSdkVersion 14 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' }
apply plugin: 'com.android.application'
这就表示咱们引入了Android的插件了,下面来演示一下最简单的自定义插件步骤。并发
事实上全部的自定义插件都须要继承一个plugin类,而后重写apply方法,以下:app
apply plugin: com.atom.MyPlugin
eclipse
class MyPlugin implements Plugin<Project>{ @Override void apply(Project project) { println "myPlugin invoked!" } }
把上述代码加到build.gradle下面,在命令行运行随意的命令:gradlew clean(windows)
调用成功了,固然这是最简单的方式,不过理解这里就能继续看AndroidDSL了
对于自定义插件的步骤我就偷偷懒了,直接给个连接吧
https://blog.csdn.net/huachao1001/article/details/51810328
对于如何查看源码,还得感谢刚哥星球的大牛们,其实很简单,只须要把全局build.gradle里的classpath的依赖加入项目build.gradle文件的dependencies里就行了,以下图:
这样就能在项目的依赖树里找到源码了,能够选择复制出来看,也能够直接在AS里看,我的感受AS也挺方便的
打开第一个,就能看见不少plugin展示在咱们眼前了,咱们最熟悉的就是AppPlugin和LibraryPlugin了
前者就是主项目须要依赖的插件,后者就是组件化的module须要依赖的插件
咱们拿最经常使用的AppPlugin来讲把,根据上面定义插件的步骤,咱们就直接看apply方法,因为Appplugin继承了basePlugin,因此又转到basePlugin:
public void apply(@NonNull Project project) { //省略一些初始化及错误检查代码
//初始化线程信息记录者 threadRecorder = ThreadRecorder.get(); //保存一些基础信息 ProcessProfileWriter.getProject(project.getPath()) .setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION) .setAndroidPlugin(getAnalyticsPluginType()) .setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST) .setOptions(AnalyticsUtil.toProto(projectOptions)); BuildableArtifactImpl.Companion.disableResolution(); //判断是否是新的API,这里咱们只看最新实现,老的就很少说了 if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) { TaskInputHelper.enableBypass(); threadRecorder.record( ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE, project.getPath(), null, this::configureProject); threadRecorder.record( ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION, project.getPath(), null, this::configureExtension); threadRecorder.record( ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION, project.getPath(), null, this::createTasks); } else { //省略之前的实现 } }
其实最重要的实如今于调用了三次threadRecorder.record,值得一说的是:this::configureProject这种写法
这是JAVA8里lambda语法,等于:()-> this.configureProject(),匿名内部类的简写方式,后面会回调这里。
J8已经出来好久了,相信你们有了必定的了解,这里就很少说。
咱们就来看看这个record方法:
@Override public void record( @NonNull ExecutionType executionType, @NonNull String projectPath, @Nullable String variant, @NonNull VoidBlock block) { //刚刚初始化过的单例 ProfileRecordWriter profileRecordWriter = ProcessProfileWriter.get(); //建立GradleBuildProfileSpan的建造者 GradleBuildProfileSpan.Builder currentRecord = create(profileRecordWriter, executionType, null); try { //刚刚提到的回调 block.call(); } catch (IOException e) { throw new UncheckedIOException(e); } finally { //写入GradleBuildProfileSpan并保存 write(profileRecordWriter, currentRecord, projectPath, variant); } }
以上代码作了以下事情:
一、建立GradleBuildProfileSpan.Builder
二、回调方法
三、写入GradleBuildProfileSpan并保存到spans中
咱们先无论回调,看一、3的代码,首先create:
private GradleBuildProfileSpan.Builder create( @NonNull ProfileRecordWriter profileRecordWriter, @NonNull ExecutionType executionType, @Nullable GradleTransformExecution transform) { long thisRecordId = profileRecordWriter.allocateRecordId(); // am I a child ? @Nullable Long parentId = recordStacks.get().peek(); long startTimeInMs = System.currentTimeMillis(); final GradleBuildProfileSpan.Builder currentRecord = GradleBuildProfileSpan.newBuilder() .setId(thisRecordId) .setType(executionType) .setStartTimeInMs(startTimeInMs); if (transform != null) { currentRecord.setTransform(transform); } if (parentId != null) { currentRecord.setParentId(parentId); } currentRecord.setThreadId(threadId.get()); recordStacks.get().push(thisRecordId); return currentRecord; }
代码很多,可是作的事情很简单,就是建立了一个GradleBuildProfileSpan.Builder,并设置了它的threadId、Id、parentId...等等一系列线程相关的东西,并保存在一个双向队列里,并放入threadLocal里解决多线程并发问题。这个threadLocal若不理解的能够移步个人另外一篇文章:消息机制:Handler源码解析
接下来是write
private void write( @NonNull ProfileRecordWriter profileRecordWriter, @NonNull GradleBuildProfileSpan.Builder currentRecord, @NonNull String projectPath, @Nullable String variant) { // pop this record from the stack. if (recordStacks.get().pop() != currentRecord.getId()) { Logger.getLogger(ThreadRecorder.class.getName()) .log(Level.SEVERE, "Profiler stack corrupted"); } currentRecord.setDurationInMs( System.currentTimeMillis() - currentRecord.getStartTimeInMs()); profileRecordWriter.writeRecord(projectPath, variant, currentRecord); }
调用了profileRecordWriter.writeRecord,继续:
/** Append a span record to the build profile. Thread safe. */ @Override public void writeRecord( @NonNull String project, @Nullable String variant, @NonNull final GradleBuildProfileSpan.Builder executionRecord) { executionRecord.setProject(mNameAnonymizer.anonymizeProjectPath(project)); executionRecord.setVariant(mNameAnonymizer.anonymizeVariant(project, variant)); spans.add(executionRecord.build()); }
这里使用建造者模式建立了GradleBuildProfileSpan,并保存到了spans里。
关于一、3步骤说了这么多,其实也就是作了这点事情,接下来才是重点了,关于回调:
回头看basePlugin里的3个回调方法configureProject、configureExtension、
createTasks,方法里传的type已经暴露了他们的做用:
一、BASE_PLUGIN_PROJECT_CONFIGURE:plugin的基础设置、初始化工做
二、BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION:EXTENSION的初始化工做
三、BASE_PLUGIN_PROJECT_TASKS_CREATION:plugin的task建立
这三步基本囊括了自定义插件的全部内容,因为篇幅缘由,我这里简单先介绍一下第一步,后面再详细解析很重要的后面两步
private void configureProject() { final Gradle gradle = project.getGradle(); extraModelInfo = new ExtraModelInfo(project.getPath(), projectOptions, project.getLogger()); checkGradleVersion(project, getLogger(), projectOptions); sdkHandler = new SdkHandler(project, getLogger()); if (!gradle.getStartParameter().isOffline() && projectOptions.get(BooleanOption.ENABLE_SDK_DOWNLOAD)) { SdkLibData sdkLibData = SdkLibData.download(getDownloader(), getSettingsController()); sdkHandler.setSdkLibData(sdkLibData); } androidBuilder = new AndroidBuilder( project == project.getRootProject() ? project.getName() : project.getPath(), creator, new GradleProcessExecutor(project), new GradleJavaProcessExecutor(project), extraModelInfo.getSyncIssueHandler(), extraModelInfo.getMessageReceiver(), getLogger(), isVerbose()); dataBindingBuilder = new DataBindingBuilder(); dataBindingBuilder.setPrintMachineReadableOutput( SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE); if (projectOptions.hasRemovedOptions()) { androidBuilder .getIssueReporter() .reportWarning(Type.GENERIC, projectOptions.getRemovedOptionsErrorMessage()); } if (projectOptions.hasDeprecatedOptions()) { extraModelInfo .getDeprecationReporter() .reportDeprecatedOptions(projectOptions.getDeprecatedOptions()); } // Apply the Java plugin project.getPlugins().apply(JavaBasePlugin.class); project.getTasks() .getByName("assemble") .setDescription( "Assembles all variants of all applications and secondary packages."); // call back on execution. This is called after the whole build is done (not // after the current project is done). // This is will be called for each (android) projects though, so this should support // being called 2+ times. gradle.addBuildListener( new BuildListener() { @Override public void buildStarted(@NonNull Gradle gradle) { TaskInputHelper.enableBypass(); BuildableArtifactImpl.Companion.disableResolution(); } @Override public void settingsEvaluated(@NonNull Settings settings) {} @Override public void projectsLoaded(@NonNull Gradle gradle) {} @Override public void projectsEvaluated(@NonNull Gradle gradle) {} @Override public void buildFinished(@NonNull BuildResult buildResult) { // Do not run buildFinished for included project in composite build. if (buildResult.getGradle().getParent() != null) { return; } sdkHandler.unload(); threadRecorder.record( ExecutionType.BASE_PLUGIN_BUILD_FINISHED, project.getPath(), null, () -> { WorkerActionServiceRegistry.INSTANCE .shutdownAllRegisteredServices( ForkJoinPool.commonPool()); PreDexCache.getCache() .clear( FileUtils.join( project.getRootProject().getBuildDir(), FD_INTERMEDIATES, "dex-cache", "cache.xml"), getLogger()); Main.clearInternTables(); }); } }); gradle.getTaskGraph() .addTaskExecutionGraphListener( taskGraph -> { TaskInputHelper.disableBypass(); Aapt2DaemonManagerService.registerAaptService( Objects.requireNonNull(androidBuilder.getTargetInfo()) .getBuildTools(), loggerWrapper, WorkerActionServiceRegistry.INSTANCE); for (Task task : taskGraph.getAllTasks()) { if (task instanceof TransformTask) { Transform transform = ((TransformTask) task).getTransform(); if (transform instanceof DexTransform) { PreDexCache.getCache() .load( FileUtils.join( project.getRootProject() .getBuildDir(), FD_INTERMEDIATES, "dex-cache", "cache.xml")); break; } } } }); createLintClasspathConfiguration(project); }
这个方法主要作了如下几件事情:
一、利用project,初始化了sdkHandler、androidBuilder、dataBindingBuilder等几个必备的对象。
二、依赖了JavaBasePlugin,这个很重要,咱们打包须要的“assemble”task就是在其中建立的。
三、对gradle建立作了监听,作了内存、磁盘缓存的工做,你能够在build\intermediates\dex-cache\cache.xml文件下找到JAR包等内容的缓存。
本文简单介绍了自定义插件内容,以及如何简单查看Android plugin的源码(固然你也能够下载Android源码查看),并简单梳理了一下插件的执行流程,引出了extensions、task等gradle中较为重要的概念。
后面再一步一步梳理源码,同时介绍gradle部分重要概念,让咱们在学习源码的同时,更加深刻理解gradle的奥妙。