diff --git a/blogs/Android/Framework/APK 构建流程.md b/blogs/Android/Framework/APK 构建流程.md new file mode 100644 index 0000000..dd4016c --- /dev/null +++ b/blogs/Android/Framework/APK 构建流程.md @@ -0,0 +1,297 @@ +--- +Android APK 构建流程 +--- + +#### 目录 + +2. 前言 +3. 构建流程 +4. Android Gradle Plugin 源码分析 + +#### 前言 + +本篇文章大量摘自 [Apk 打包流程梳理](https://juejin.im/entry/58b78d1b61ff4b006cd47e5b) 以及其文章中的参考文章,我觉得你只要把那篇文章及其参考文看完,熟悉 APK 构建流程和安装流程完全不在话下。 + +当然,看完文章之后,你可以试试自己画一下构建流程图。 + +本篇主要用来记录学习过程,再次感谢大佬们的分享精神~ + +#### 构建流程 + +先来一张 [Android 配置构建]() 官网上的一张图: + +![](https://i.loli.net/2019/05/07/5cd1888bcfafa.png) + +这个新版的图,比较好理解,就不过阐述了,我还是喜欢老版的经典构建图: + +![](https://i.loli.net/2019/05/07/5cd14ad86262c.png) + +从上面的流程图,我可以看出 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) + +详细构建流程图: + +![](https://i.loli.net/2019/05/08/5cd2a35578f65.png) + +#### Android Gradle Plugin + +有了以上的初步了解之后,再来看看 Android Gradle Plugin 是如何实现构建流程的。 + +我们使用的 apply plugin: 'com.android.application' 和 apply plugin:'com.android.library' 插件对应的类分别是 AppPlugin 和 LibraryPlugin。它们都是继承 BasePlugin: + +```java +public abstract class BasePlugin implements Plugin{ + @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 buildTypeContainer = + project.container()); + final NamedDomainObjectContainer productFlavorContainer = + project.container()); + final NamedDomainObjectContainer signingConfigContainer = + project.container()); + + final NamedDomainObjectContainer 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 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. + // + //

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 = + 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) \ No newline at end of file diff --git a/blogs/Android/Framework/APK 构建流程以及 APK 安装流程.md b/blogs/Android/Framework/APK 构建流程以及 APK 安装流程.md deleted file mode 100644 index 4629c43..0000000 --- a/blogs/Android/Framework/APK 构建流程以及 APK 安装流程.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -Android APK 构建流程以及 APK 安装流程 ---- - -#### 目录 - -1. 思维大图 -2. 前言 -3. 构建流程 -4. 安装流程 - -#### 思维导图 - -#### 前言 - -本篇文章大量摘自 [Apk 打包流程梳理](https://juejin.im/entry/58b78d1b61ff4b006cd47e5b) 以及其文章中的参考文章,我觉得你只要把那篇文章及其参考文看完,熟悉 APK 构建流程和安装流程完全不在话下。 - -当然,看完文章之后,你可以试试自己画一下构建流程图。 - -本篇主要用来记录学习过程,再次感谢大佬们的分享精神~ - -#### 构建流程 - -先来一张 [Android 配置构建]() 官网上的一张图: - -![](https://i.loli.net/2019/05/07/5cd1888bcfafa.png) - -这个新版的图,比较好理解,就不过阐述了,我还是喜欢老版的经典构建图: - -![](https://i.loli.net/2019/05/07/5cd14ad86262c.png) - -从上面的流程图,我可以看出 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) - -#### 安装流程 - -#### 参考 - diff --git a/images/Android/Framework/APK 详细构建流程图.png b/images/Android/Framework/APK 详细构建流程图.png new file mode 100644 index 0000000..c5ab0b4 Binary files /dev/null and b/images/Android/Framework/APK 详细构建流程图.png differ