parent
							
								
									27d8cd2ac6
								
							
						
					
					
						commit
						68e3588217
					
				| @ -0,0 +1,297 @@ | ||||
| --- | ||||
| Android APK 构建流程 | ||||
| --- | ||||
| 
 | ||||
| #### 目录 | ||||
| 
 | ||||
| 2. 前言 | ||||
| 3. 构建流程 | ||||
| 4. Android Gradle Plugin 源码分析 | ||||
| 
 | ||||
| #### 前言 | ||||
| 
 | ||||
| 本篇文章大量摘自 [Apk 打包流程梳理](https://juejin.im/entry/58b78d1b61ff4b006cd47e5b) 以及其文章中的参考文章,我觉得你只要把那篇文章及其参考文看完,熟悉 APK 构建流程和安装流程完全不在话下。 | ||||
| 
 | ||||
| 当然,看完文章之后,你可以试试自己画一下构建流程图。 | ||||
| 
 | ||||
| 本篇主要用来记录学习过程,再次感谢大佬们的分享精神~ | ||||
| 
 | ||||
| #### 构建流程 | ||||
| 
 | ||||
| 先来一张 [Android 配置构建]() 官网上的一张图: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 这个新版的图,比较好理解,就不过阐述了,我还是喜欢老版的经典构建图: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 从上面的流程图,我可以看出 APK 构建流程可以分为以下七步: | ||||
| 
 | ||||
| 1. 通过 aapt 打包 res 资源文件,生成 R.java、resources.arsc 和 res 文件(二进制和非二进制如 res/raw 和 pic 保持原样) | ||||
| 2. 处理 .aidl 文件,生成对应的 Java 接口文件 | ||||
| 3. 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成 .class 文件 | ||||
| 4. 通过 dx/d8 工具,将 .class 文件和第三方库中的 .class 文件处理生成 classes.dex | ||||
| 5. 通过 apkbuilder 工具,将 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk | ||||
| 6. 通过 Jarsigner 工具,对上面的 apk 进行 debug 或 release 签名 | ||||
| 7. 通过 zipalign 工具,对签名后的 apk 进行对齐处理 | ||||
| 
 | ||||
| ##### 第一步:aapt | ||||
| 
 | ||||
| aapt(Android Asset Packaging Tool),用于打包资源文件,生成 R.java 和编译后的资源。aapt 的源码位于 frameworks/base/tools/aapt2 目录下。 | ||||
| 
 | ||||
| ##### 第二步:aidl | ||||
| 
 | ||||
| 用于处理 aidl 文件,源码位于 frameworks/base/tools/aidl。输入 aidl 后缀的文件,输出可用于进程通信的 C/S 端 Java 代码,位于 build/generated/source/aidl。 | ||||
| 
 | ||||
| ##### 第三步:Java 源码编译 | ||||
| 
 | ||||
| 有了 R.java 和 aidl 生成的 Java 文件,再加上工程的源代码,现在就可以使用 javac 进行正常的 java 编译生成 class 文件了。 | ||||
| 
 | ||||
| 输入:java source 的文件夹(另外还包括了 build/generated 下的:R.java,aidl 生成的 Java 文件以及 BuildConfig.java) | ||||
| 
 | ||||
| 输出:对于 gradle 编译,可以在 build/intermediates/classes 里,看到输出的 class 文件。 | ||||
| 
 | ||||
| ##### 第四步:dex | ||||
| 
 | ||||
| 使用 dx/d8 工具将 class 文件转化为 dex 文件,生成常量池,消除冗余数据等。 | ||||
| 
 | ||||
| ##### 第五步:apkbuilder | ||||
| 
 | ||||
| 打包生成 APK 文件,现在都已经通过 sdklib.jar 的 ApkBuilder 类进行打包了,输入为我们之前生成的包含 resources.arsc 的 .ap_ 文件,上一步生成的 dex 文件,以及其他资源如 jni、jar 包内的资源。 | ||||
| 
 | ||||
| ##### 第六步:Jarsigner | ||||
| 
 | ||||
| 对 apk 文件进行签名,APK 需要签名才能在设备上进行安装。现在不仅可以使用 jarsigner,还可以使用 apksigner。 | ||||
| 
 | ||||
| ##### 第七步:zipalign | ||||
| 
 | ||||
| 对签名后的 apk 文件进行对齐处理,使得 apk 中所有资源文件距离文件起始偏移为四个字节的整数倍,从而在通过内存映射访问 apk 文件时会更快。 | ||||
| 
 | ||||
| 需要注意的,使用不同的签名工具对对齐是有影响的。如果使用 jarsigner,则只能在 APK 文件签名后执行 zipalign;如果使用 apksigner,则只能在 APK 文件签名之前执行 zipalign。 | ||||
| 
 | ||||
| 详见官网描述:[zipalign](https://developer.android.com/studio/command-line/zipalign) | ||||
| 
 | ||||
| 详细构建流程图: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| #### Android Gradle Plugin  | ||||
| 
 | ||||
| 有了以上的初步了解之后,再来看看 Android Gradle Plugin 是如何实现构建流程的。 | ||||
| 
 | ||||
| 我们使用的 apply plugin: 'com.android.application' 和 apply plugin:'com.android.library' 插件对应的类分别是 AppPlugin 和 LibraryPlugin。它们都是继承 BasePlugin: | ||||
| 
 | ||||
| ```java | ||||
| public abstract class BasePlugin<E extends BaseExtension2> implements Plugin<Project>{ | ||||
|     @Override | ||||
|     public final void apply(@NonNull Project project) { | ||||
|       	//核心方法 | ||||
|         basePluginApply(project); | ||||
|       	//因为 apply 是 final | ||||
|       	//所以提供一个 pluginSpecificApply 用于 AppPlugin 和 LibraryPlugin 加入特殊逻辑 | ||||
|         pluginSpecificApply(project); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 看到重写的 apply 方法是 final,就很开心了,也就是说我们只需要分析 BasePlugin 里面的逻辑就好了。核心方法是 basePluginApply,源码如下: | ||||
| 
 | ||||
| ```java | ||||
|     private void basePluginApply(@NonNull Project project) { | ||||
|       	//核心三个方法 | ||||
| 				configureProject(); | ||||
|       	configureExtension(); | ||||
| 				createTasks(); | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| ##### configureProject() | ||||
| 
 | ||||
| ``` | ||||
|     private void configureProject() { | ||||
|    			//主构建器类,它提供了处理构建的所有数据,比如 DefaultProductFlavor、DefaultBuildType 以及一些依赖项,在执行的时候使用它们特定的构建步骤 | ||||
|         AndroidBuilder androidBuilder = new AndroidBuilder(); | ||||
| 				//... | ||||
|         // 依赖 Java 插件 | ||||
|         project.getPlugins().apply(JavaBasePlugin.class); | ||||
| 				//Plugin 的全局变量 | ||||
|         new GlobalScope() | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| ##### configureExtension() | ||||
| 
 | ||||
| ```java | ||||
|     private void configureExtension() { | ||||
|         ObjectFactory objectFactory = project.getObjects(); | ||||
|       	//创建 BuildType、ProductFlavor、SigningConfig、BaseVariantOutput 的容器 | ||||
|         final NamedDomainObjectContainer<BuildType> buildTypeContainer = | ||||
|                 project.container()); | ||||
|         final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer = | ||||
|                 project.container()); | ||||
|         final NamedDomainObjectContainer<SigningConfig> signingConfigContainer = | ||||
|                 project.container()); | ||||
| 
 | ||||
|         final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs = | ||||
|                 project.container(BaseVariantOutput.class); | ||||
| 
 | ||||
|         project.getExtensions().add("buildOutputs", buildOutputs); | ||||
| 
 | ||||
|         sourceSetManager = new SourceSetManager(); | ||||
| 				//创建 BaseExtension | ||||
|         extension = createExtension(); | ||||
| 
 | ||||
|         globalScope.setExtension(extension); | ||||
| 
 | ||||
|         variantFactory = createVariantFactory(globalScope, extension); | ||||
| 				//创建 TaskManager | ||||
|         taskManager = createTaskManager(); | ||||
| 				//创建 VariantManager | ||||
|         variantManager = new VariantManager(); | ||||
| 
 | ||||
|         registerModels(registry, globalScope, variantManager, extension, extraModelInfo); | ||||
|         variantFactory.createDefaultComponents( | ||||
|                 buildTypeContainer, productFlavorContainer, signingConfigContainer); | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| ##### createTask() | ||||
| 
 | ||||
| ```java | ||||
|     private void createTasks() { | ||||
|       	//注册一些默认 Task,比如 uninstallAll、deviceCheck、connectedCheck 等 | ||||
|         taskManager.createTasksBeforeEvaluate() | ||||
| 
 | ||||
|         createAndroidTasks(); | ||||
|     } | ||||
| 
 | ||||
|     final void createAndroidTasks() { | ||||
|       	//... | ||||
|         List<VariantScope> variantScopes = variantManager.createAndroidTasks(); | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| 然后就一直到了 TaskManager#createTasksForVariantScope 方法,它是一个抽象方法,我们看它在 ApplicationTaskManager 中的实现: | ||||
| 
 | ||||
| ```java | ||||
|     public void createTasksForVariantScope(@NonNull final VariantScope variantScope) { | ||||
|         createAnchorTasks(variantScope); | ||||
|       	//checkXxxManifest 检查 Manifest 文件存在、路径 | ||||
|         createCheckManifestTask(variantScope); | ||||
| 
 | ||||
|         handleMicroApp(variantScope); | ||||
| 
 | ||||
|         //把依赖放到 TransformManager 中 | ||||
|         createDependencyStreams(variantScope); | ||||
| 
 | ||||
|         // Add a task to publish the applicationId. | ||||
|         createApplicationIdWriterTask(variantScope); | ||||
| 
 | ||||
|         // 合并 manifest | ||||
|         createMergeApkManifestsTask(variantScope); | ||||
| 
 | ||||
|         // Add a task to create the res values | ||||
|         createGenerateResValuesTask(variantScope); | ||||
| 
 | ||||
|         // Add a task to compile renderscript files. | ||||
|         createRenderscriptTask(variantScope); | ||||
| 
 | ||||
|         // 合并 resource | ||||
|         createMergeResourcesTask( | ||||
|                 variantScope, | ||||
|                 true, | ||||
|                 Sets.immutableEnumSet(MergeResources.Flag.PROCESS_VECTOR_DRAWABLES)); | ||||
| 
 | ||||
|         // 合并 assets 文件夹 | ||||
|         createMergeAssetsTask(variantScope); | ||||
| 
 | ||||
|         // 生成 BuildConfig.java 文件 | ||||
|         createBuildConfigTask(variantScope); | ||||
| 
 | ||||
|         // Add a task to process the Android Resources and generate source files | ||||
|         createApkProcessResTask(variantScope); | ||||
| 
 | ||||
|         // Add a task to process the java resources | ||||
|         createProcessJavaResTask(variantScope); | ||||
| 
 | ||||
|         createAidlTask(variantScope); | ||||
| 
 | ||||
|         // Add external native build tasks | ||||
|         createExternalNativeBuildJsonGenerators(variantScope); | ||||
|         createExternalNativeBuildTasks(variantScope); | ||||
| 
 | ||||
|         // Add a task to merge the jni libs folders | ||||
|         createMergeJniLibFoldersTasks(variantScope); | ||||
| 
 | ||||
|         // Add feature related tasks if necessary | ||||
|         if (variantScope.getType().isBaseModule()) { | ||||
|             // Base feature specific tasks. | ||||
|             taskFactory.register(new FeatureSetMetadataWriterTask.CreationAction(variantScope)); | ||||
| 
 | ||||
|             createValidateSigningTask(variantScope); | ||||
|             // Add a task to produce the signing config file. | ||||
|             taskFactory.register(new SigningConfigWriterTask.CreationAction(variantScope)); | ||||
| 
 | ||||
|             if (extension.getDataBinding().isEnabled()) { | ||||
|                 // Create a task that will package the manifest ids(the R file packages) of all | ||||
|                 // features into a file. This file's path is passed into the Data Binding annotation | ||||
|                 // processor which uses it to known about all available features. | ||||
|                 // | ||||
|                 // <p>see: {@link TaskManager#setDataBindingAnnotationProcessorParams(VariantScope)} | ||||
|                 taskFactory.register( | ||||
|                         new DataBindingExportFeatureApplicationIdsTask.CreationAction( | ||||
|                                 variantScope)); | ||||
| 
 | ||||
|             } | ||||
|         } else { | ||||
|             // Non-base feature specific task. | ||||
|             // Task will produce artifacts consumed by the base feature | ||||
|             taskFactory.register( | ||||
|                     new FeatureSplitDeclarationWriterTask.CreationAction(variantScope)); | ||||
|             if (extension.getDataBinding().isEnabled()) { | ||||
|                 // Create a task that will package necessary information about the feature into a | ||||
|                 // file which is passed into the Data Binding annotation processor. | ||||
|                 taskFactory.register( | ||||
|                         new DataBindingExportFeatureInfoTask.CreationAction(variantScope)); | ||||
|             } | ||||
|             taskFactory.register(new MergeConsumerProguardFilesTask.CreationAction(variantScope)); | ||||
|         } | ||||
| 
 | ||||
|         // Add data binding tasks if enabled | ||||
|         createDataBindingTasksIfNecessary(variantScope, MergeType.MERGE); | ||||
| 
 | ||||
|         // Add a compile task | ||||
|         createCompileTask(variantScope); | ||||
| 
 | ||||
|         createStripNativeLibraryTask(taskFactory, variantScope); | ||||
| 
 | ||||
| 
 | ||||
|         if (variantScope.getVariantData().getMultiOutputPolicy().equals(MultiOutputPolicy.SPLITS)) { | ||||
|             if (extension.getBuildToolsRevision().getMajor() < 21) { | ||||
|                 throw new RuntimeException( | ||||
|                         "Pure splits can only be used with buildtools 21 and later"); | ||||
|             } | ||||
| 
 | ||||
|             createSplitTasks(variantScope); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         TaskProvider<BuildInfoWriterTask> buildInfoWriterTask = | ||||
|                 createInstantRunPackagingTasks(variantScope); | ||||
|         createPackagingTask(variantScope, buildInfoWriterTask); | ||||
| 
 | ||||
|         // Create the lint tasks, if enabled | ||||
|         createLintTasks(variantScope); | ||||
| 
 | ||||
|         taskFactory.register(new FeatureSplitTransitiveDepsWriterTask.CreationAction(variantScope)); | ||||
| 
 | ||||
|         createDynamicBundleTask(variantScope); | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| #### 参考 | ||||
| 
 | ||||
| [Apk 打包流程梳理](https://juejin.im/entry/58b78d1b61ff4b006cd47e5b) | ||||
| 
 | ||||
| [Android App 编译流程详解](https://gitbook.cn/gitchat/activity/5cb3f38f6f093c03b1bfe0f5) | ||||
| @ -1,81 +0,0 @@ | ||||
| --- | ||||
| Android APK 构建流程以及 APK 安装流程 | ||||
| --- | ||||
| 
 | ||||
| #### 目录 | ||||
| 
 | ||||
| 1. 思维大图 | ||||
| 2. 前言 | ||||
| 3. 构建流程 | ||||
| 4. 安装流程 | ||||
| 
 | ||||
| #### 思维导图 | ||||
| 
 | ||||
| #### 前言 | ||||
| 
 | ||||
| 本篇文章大量摘自 [Apk 打包流程梳理](https://juejin.im/entry/58b78d1b61ff4b006cd47e5b) 以及其文章中的参考文章,我觉得你只要把那篇文章及其参考文看完,熟悉 APK 构建流程和安装流程完全不在话下。 | ||||
| 
 | ||||
| 当然,看完文章之后,你可以试试自己画一下构建流程图。 | ||||
| 
 | ||||
| 本篇主要用来记录学习过程,再次感谢大佬们的分享精神~ | ||||
| 
 | ||||
| #### 构建流程 | ||||
| 
 | ||||
| 先来一张 [Android 配置构建]() 官网上的一张图: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 这个新版的图,比较好理解,就不过阐述了,我还是喜欢老版的经典构建图: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 从上面的流程图,我可以看出 APK 构建流程可以分为以下七步: | ||||
| 
 | ||||
| 1. 通过 aapt 打包 res 资源文件,生成 R.java、resources.arsc 和 res 文件(二进制和非二进制) | ||||
| 2. 处理 .aidl 文件,生成对应的 Java 接口文件 | ||||
| 3. 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成 .class 文件 | ||||
| 4. 通过 dx/d8 工具,将 .class 文件和第三方库中的 .class 文件处理生成 classes.dex | ||||
| 5. 通过 apkbuilder 工具,将 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk | ||||
| 6. 通过 Jarsigner 工具,对上面的 apk 进行 debug 或 release 签名 | ||||
| 7. 通过 zipalign 工具,对签名后的 apk 进行对齐处理 | ||||
| 
 | ||||
| ##### 第一步:aapt | ||||
| 
 | ||||
| aapt(Android Asset Packaging Tool),用于打包资源文件,生成 R.java 和编译后的资源。aapt 的源码位于 frameworks/base/tools/aapt2 目录下。 | ||||
| 
 | ||||
| ##### 第二步:aidl | ||||
| 
 | ||||
| 用于处理 aidl 文件,源码位于 frameworks/base/tools/aidl。输入 aidl 后缀的文件,输出可用于进程通信的 C/S 端 Java 代码,位于 build/generated/source/aidl。 | ||||
| 
 | ||||
| ##### 第三步:Java 源码编译 | ||||
| 
 | ||||
| 有了 R.java 和 aidl 生成的 Java 文件,再加上工程的源代码,现在就可以使用 javac 进行正常的 java 编译生成 class 文件了。 | ||||
| 
 | ||||
| 输入:java source 的文件夹(另外还包括了 build/generated 下的:R.java,aidl 生成的 Java 文件以及 BuildConfig.java) | ||||
| 
 | ||||
| 输出:对于 gradle 编译,可以在 build/intermediates/classes 里,看到输出的 class 文件。 | ||||
| 
 | ||||
| ##### 第四步:dex | ||||
| 
 | ||||
| 使用 dx/d8 工具将 class 文件转化为 dex 文件,生成常量池,消除冗余数据等。 | ||||
| 
 | ||||
| ##### 第五步:apkbuilder | ||||
| 
 | ||||
| 打包生成 APK 文件,现在都已经通过 sdklib.jar 的 ApkBuilder 类进行打包了,输入为我们之前生成的包含 resources.arsc 的 .ap_ 文件,上一步生成的 dex 文件,以及其他资源如 jni、jar 包内的资源。 | ||||
| 
 | ||||
| ##### 第六步:Jarsigner | ||||
| 
 | ||||
| 对 apk 文件进行签名,APK 需要签名才能在设备上进行安装。现在不仅可以使用 jarsigner,还可以使用 apksigner。 | ||||
| 
 | ||||
| ##### 第七步:zipalign | ||||
| 
 | ||||
| 对签名后的 apk 文件进行对齐处理,使得 apk 中所有资源文件距离文件起始偏移为四个字节的整数倍,从而在通过内存映射访问 apk 文件时会更快。 | ||||
| 
 | ||||
| 需要注意的,使用不同的签名工具对对齐是有影响的。如果使用 jarsigner,则只能在 APK 文件签名后执行 zipalign;如果使用 apksigner,则只能在 APK 文件签名之前执行 zipalign。 | ||||
| 
 | ||||
| 详见官网描述:[zipalign](https://developer.android.com/studio/command-line/zipalign) | ||||
| 
 | ||||
| #### 安装流程 | ||||
| 
 | ||||
| #### 参考 | ||||
| 
 | ||||
| After Width: | Height: | Size: 131 KiB | 
					Loading…
					
					
				
		Reference in new issue