Android构建流程——篇七
文章目录
- Task24 transformClassesWithDexBuilderForDebug
- 1.input/ouput
- 2. 核心类(DexArchiveBuilderTransform,TransformTask)
- 2.1 processJarInput
- 2.2. convertToDexArchive
- 2.3 launchProcessing
- 3. 梳理下总体调用链路
- Task25 transformClassesWithMultidexlistForDebug
- 1. input/ouput
- 2. 核心类(D8MainDexListTransform)
- Task26 transformDexArchiveWithDexMergerForDebug
- 1. input/ouput
- 2. 核心类(DexMergerTransform)
- Task 27 mergeDebugJniLibFolders
- 1. input/output
- 2. 核心类(MergeJniLibFoldersConfigAction, MergeSourceSetFolders)
- Task28 transformNativeLibsWithMergeJniLibsForDebug
- 1. input/output
- 2. 核心类(MergeJavaResourcesTransform)
- 3. transform
Task24 transformClassesWithDexBuilderForDebug
1.input/ouput
taskName:transformClassesWithDexBuilderForDebug
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/adapters-3.2.0.aar/49b3d7e4ab68d92f056ea8f56b33e9fb/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/library-3.2.0.aar/818ffb3fe5dc5eeb6b4e51c93615b7fb/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.databinding/baseLibrary/3.2.0/fb5f8492c36231104cd86feaefa723291504c0a6/baseLibrary-3.2.0.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.3.aar/5765c0929bc6bc40d70d6fc25f402f42/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.3/7d7f60c4783872861222166f6164215f8951c7b1/common-1.0.3.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/support/coreutils/R.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/support/coreutils/R$bool.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/cl/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/databinding/layouts/DataBindingInfo.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/databinding/DataBindingComponent.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/MainActivity.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R$bool.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R$id.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/IHelloAidlInterface$Stub.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R$mipmap.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/DataBinderMapperImpl.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/DataBinderMapperImpl$InnerBrLookup.class
省略...
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/transforms/dexBuilder/debug
从日志不难发现,输入数据格式包含二类**.jar和javac编译后.class**文件,该任务主要做了二件事情
- 将每个class文件通过D8编译器(AS3.0新增的D8默认开启)编译成dex文件
- 对于jar,则将其中的class编译成dex再打包成jar
关于D8功效果描述参加官方描述https://developer.android.com/studio/command-line/d8?hl=zh-cn
2. 核心类(DexArchiveBuilderTransform,TransformTask)
/*** Transform that converts CLASS files to dex archives, {@link* com.android.builder.dexing.DexArchive}. This will consume {@link TransformManager#CONTENT_CLASS},* and for each of the inputs, corresponding dex archive will be produced.** <p>This transform is incremental, only changed streams will be converted again. Additionally, if* an input stream is able to provide a list of individual files that were changed, only those files* will be processed. Their corresponding dex archives will be updated.*/
public class DexArchiveBuilderTransform extends Transform {@Overridepublic void transform(@NonNull TransformInvocation transformInvocation)throws TransformException, IOException, InterruptedException {List<DexArchiveBuilderCacheHandler.CacheableItem> cacheableItems = new ArrayList<>();....for (TransformInput input : transformInvocation.getInputs()) {for (DirectoryInput dirInput : input.getDirectoryInputs()) {logger.verbose("Dir input %s", dirInput.getFile().toString());// 1. 扫描Dir目录对该目录下的所有class文件进行D8编译处理生产dex文件convertToDexArchive(transformInvocation.getContext(),dirInput,outputProvider,isIncremental,bootclasspathServiceKey,classpathServiceKey,additionalPaths);}for (JarInput jarInput : input.getJarInputs()) {logger.verbose("Jar input %s", jarInput.getFile().toString());// 2. 对项目中依赖的jar进行处理D8DesugaringCacheInfo cacheInfo =getD8DesugaringCacheInfo(desugarIncrementalTransformHelper,bootclasspath,classpath,jarInput);List<File> dexArchives =processJarInput(transformInvocation.getContext(),isIncremental,jarInput,outputProvider,bootclasspathServiceKey,classpathServiceKey,additionalPaths,cacheInfo);if (cacheInfo != D8DesugaringCacheInfo.DONT_CACHE && !dexArchives.isEmpty()) {cacheableItems.add(new DexArchiveBuilderCacheHandler.CacheableItem(jarInput,dexArchives,cacheInfo.orderedD8DesugaringDependencies));}}}// all work items have been submitted, now wait for completion.if (useGradleWorkers) {transformInvocation.getContext().getWorkerExecutor().await();} else {executor.waitForTasksWithQuickFail(true);}// if we are in incremental mode, delete all removed files.if (transformInvocation.isIncremental()) {for (TransformInput transformInput : transformInvocation.getInputs()) {removeDeletedEntries(outputProvider, transformInput);}}// 缓存对jar进行dex后的文件,存储到用户级别(/Users/${username}/.android/build-cache/{gradle_plugin_version})// 方便后续直接复用// and finally populate the caches.if (!cacheableItems.isEmpty()) {cacheHandler.populateCache(cacheableItems);}}
}
先看jar;如果是jar会调用processJarInput方法
2.1 processJarInput
@NonNull
private List<File> processJarInput(@NonNull Context context,boolean isIncremental,@NonNull JarInput jarInput,@NonNull TransformOutputProvider transformOutputProvider,@NonNull ClasspathServiceKey bootclasspath,@NonNull ClasspathServiceKey classpath,@NonNull Set<File> additionalPaths,@NonNull D8DesugaringCacheInfo cacheInfo)throws Exception {if (!isIncremental || additionalPaths.contains(jarInput.getFile())) {Preconditions.checkState(jarInput.getFile().exists(),"File %s does not exist, yet it is reported as input. Try \n"+ "cleaning the build directory.",jarInput.getFile().toString());return convertJarToDexArchive(context,jarInput,transformOutputProvider,bootclasspath,classpath,cacheInfo);} else if (jarInput.getStatus() != Status.NOTCHANGED) {// 处理增量流程,可以先忽略// delete all preDex jars if they exists....}return ImmutableList.of();
}private List<File> convertJarToDexArchive(@NonNull Context context,@NonNull JarInput toConvert,@NonNull TransformOutputProvider transformOutputProvider,@NonNull ClasspathServiceKey bootclasspath,@NonNull ClasspathServiceKey classpath,@NonNull D8DesugaringCacheInfo cacheInfo)throws Exception {if (cacheInfo != D8DesugaringCacheInfo.DONT_CACHE) {File cachedVersion =cacheHandler.getCachedVersionIfPresent(toConvert, cacheInfo.orderedD8DesugaringDependencies);// 1. 判断本地缓存(用户级别)是否有jar,如果有直接copy,复用if (cachedVersion != null) {File outputFile = getOutputForJar(transformOutputProvider, toConvert, null);Files.copy(cachedVersion.toPath(),outputFile.toPath(),StandardCopyOption.REPLACE_EXISTING);// no need to try to cache an already cached version.return ImmutableList.of();}}// 2. 没有缓存走该方法return convertToDexArchive(context,toConvert,transformOutputProvider,false,bootclasspath,classpath,ImmutableSet.of());}
可以看到无论是jar还是class他们最终都会调用同一个方法convertToDexArchive,我们看看它到底做了什么
2.2. convertToDexArchive
private List<File> convertToDexArchive(@NonNull Context context,@NonNull QualifiedContent input,@NonNull TransformOutputProvider outputProvider,boolean isIncremental,@NonNull ClasspathServiceKey bootClasspath,@NonNull ClasspathServiceKey classpath,@NonNull Set<File> additionalPaths) {logger.verbose("Dexing %s", input.getFile().getAbsolutePath());ImmutableList.Builder<File> dexArchives = ImmutableList.builder();for (int bucketId = 0; bucketId < numberOfBuckets; bucketId++) {File preDexOutputFile;//1. 如果是输入为dir,也即是javac编译保存class文件目录;生成存储dex目录if (input instanceof DirectoryInput) {preDexOutputFile =getOutputForDir(outputProvider, (DirectoryInput) input, bucketId);FileUtils.mkdirs(preDexOutputFile);} else {//2. 否则是jar,则构建一个输出${INT}.jar文件preDexOutputFile = getOutputForJar(outputProvider, (JarInput) input, bucketId);}dexArchives.add(preDexOutputFile);DexConversionParameters parameters =new DexConversionParameters(input,bootClasspath,classpath,preDexOutputFile,numberOfBuckets,bucketId,minSdkVersion,dexOptions.getAdditionalParameters(),inBufferSize,outBufferSize,dexer,isDebuggable,isIncremental,java8LangSupportType,additionalPaths,new SerializableMessageReceiver(messageReceiver),isInstantRun);if (useGradleWorkers) {context.getWorkerExecutor().submit(DexConversionWorkAction.class,configuration -> {configuration.setIsolationMode(IsolationMode.NONE);configuration.setParams(parameters);});} else {executor.execute(() -> {ProcessOutputHandler outputHandler =new ParsingProcessOutputHandler(new ToolOutputParser(new DexParser(), Message.Kind.ERROR, logger),new ToolOutputParser(new DexParser(), logger),messageReceiver);ProcessOutput output = null;try (Closeable ignored = output = outputHandler.createOutput()) {// 无论是jar还是class最终都会走该方法launchProcessing(parameters,output.getStandardOutput(),output.getErrorOutput(),messageReceiver);} finally {if (output != null) {try {outputHandler.handleOutput(output);} catch (ProcessException e) {// ignore this one}}}return null;});}}return dexArchives.build();}
可以看到无论是class还是jar最后都会执行launchProcessing方法,关键部分如下
2.3 launchProcessing
private static void launchProcessing(@NonNull DexConversionParameters dexConversionParameters,@NonNull OutputStream outStream,@NonNull OutputStream errStream,@NonNull MessageReceiver receiver)throws IOException, URISyntaxException {...logger.verbose("Dexing '" + inputPath + "' to '" + dexConversionParameters.output + "'");// 1, 此处根据inputPath判断,如果是jar返回JarClassFileInput类型,否则视为DirectoryBasedClassFileInputtry (ClassFileInput input = ClassFileInputs.fromPath(inputPath);Stream<ClassFileEntry> entries = input.entries(bucketFilter)) {// 2. 核心代码,此处调用D8DexArchiveBuilder类将jar或class文件进行编译dexArchiveBuilder.convert(entries,Paths.get(new URI(dexConversionParameters.output)),dexConversionParameters.isDirectoryBased());} catch (DexArchiveBuilderException ex) {throw new DexArchiveBuilderException("Failed to process " + inputPath.toString(), ex);}}
// D8DexArchiveBuilder.java
public void convert(@NonNull Stream<ClassFileEntry> input, @NonNull Path output, boolean isIncremental)throws DexArchiveBuilderException {D8DiagnosticsHandler d8DiagnosticsHandler = new InterceptingDiagnosticsHandler();try {D8Command.Builder builder = D8Command.builder(d8DiagnosticsHandler);AtomicInteger entryCount = new AtomicInteger();input.forEach(entry -> {//1. 读取每个class文件存储到builder,为后续D8编译器服务builder.addClassProgramData(readAllBytes(entry), D8DiagnosticsHandler.getOrigin(entry));entryCount.incrementAndGet();});if (entryCount.get() == 0) {// nothing to do here, just returnreturn;}OutputMode outputMode =isIncremental ? OutputMode.DexFilePerClassFile : OutputMode.DexIndexed;builder.setMode(compilationMode).setMinApiLevel(minSdkVersion).setIntermediate(true).setOutput(output, outputMode);if (desugaring) {builder.addLibraryResourceProvider(bootClasspath.getOrderedProvider());builder.addClasspathResourceProvider(classpath.getOrderedProvider());} else {builder.setDisableDesugaring(true);}// 2. D8编译器进行编译处理D8.run(builder.build(), MoreExecutors.newDirectExecutorService());} catch (Throwable e) {throw getExceptionToRethrow(e, d8DiagnosticsHandler);}}
3. 梳理下总体调用链路
对于AGP为什么选择只选择缓存jar,从AGP中DexArchiveBuilderCacheHandler部分注释可以找到答案,注释如下
// Don't cache libraries depending on class files in folders:// Folders content is expected to change often so probably not worth paying the cache cost// if we frequently need to rebuild anyway.// Supporting dependency to class files would also require special care to respect order.
大意认为class文件通常是经常发生变化的,缓存没有多大意义;而jar则被认为是稳定的所以做了缓存处理,缓存相关类参见DexArchiveBuilderCacheHandler,有兴趣大家自己瞅瞅
Task25 transformClassesWithMultidexlistForDebug
1. input/ouput
预备操作,build.gradle中添加
defaultConfig {multiDexEnabled truemultiDexKeepFile file('maindexlist.txt')}
先在maindexlist.txt中添加MainActivity路径让其进入主dex
askName:transformClassesWithMultidexlistForDebug
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/legacy_multidex_aapt_derived_proguard_rules/debug/processDebugResources/manifest_keep.txt
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/maindexlist.txt
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/adapters-3.2.0.aar/49b3d7e4ab68d92f056ea8f56b33e9fb/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/library-3.2.0.aar/818ffb3fe5dc5eeb6b4e51c93615b7fb/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.databinding/baseLibrary/3.2.0/fb5f8492c36231104cd86feaefa723291504c0a6/baseLibrary-3.2.0.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/multidex-1.0.2.aar/36d8b7d496f603cd6f98051792b0414c/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.3.aar/5765c0929bc6bc40d70d6fc25f402f42/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.3/7d7f60c4783872861222166f6164215f8951c7b1/common-1.0.3.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/support/coreutils/R.class
...
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/legacy_multidex_main_dex_list/debug/transformClassesWithMultidexlistForDebug/mainDexList.txt
其实该任务就是我们通常说的Android分包机制
从控制台可以看出该任务接受manifest_keep.txt、gradle中配置的multiDexKeepFile属性以及APP依赖的jar,class文件最终进行处理生成mainDexList.txt该文件就是主dex中需要包含的类的集合,流程图如下
2. 核心类(D8MainDexListTransform)
/*** Calculate the main dex list using D8.*/
class D8MainDexListTransform(private val manifestProguardRules: BuildableArtifact,private val userProguardRules: Path? = null,private val userClasses: Path? = null,private val includeDynamicFeatures: Boolean = false,private val bootClasspath: Supplier<List<Path>>) : Transform(), MainDexListWriter {private val logger = LoggerWrapper.getLogger(D8MainDexListTransform::class.java)private lateinit var outputMainDexList: Path@JvmOverloadsconstructor(variantScope: VariantScope, includeDynamicFeatures: Boolean = false) :this(variantScope.artifacts.getFinalArtifactFiles(InternalArtifactType.LEGACY_MULTIDEX_AAPT_DERIVED_PROGUARD_RULES),variantScope.variantConfiguration.multiDexKeepProguard?.toPath(),variantScope.variantConfiguration.multiDexKeepFile?.toPath(),includeDynamicFeatures,Supplier {variantScope.globalScope.androidBuilder.getBootClasspath(true).map { it.toPath() }})...override fun transform(invocation: TransformInvocation) {logger.verbose("Generating the main dex list using D8.")try {val inputs = MainDexListTransform.getByInputType(invocation)val programFiles = inputs[INPUT_JAR]!!.map { it.toPath() }val libraryFiles = inputs[LIBRARY_JAR]!!.map { it.toPath() } + bootClasspath.get()logger.verbose("Program files: %s", programFiles.joinToString())logger.verbose("Library files: %s", libraryFiles.joinToString())logger.verbose("Proguard rule files: %s",listOfNotNull(manifestProguardRules, userProguardRules).joinToString())// 1.读取混淆规则文件val proguardRules =listOfNotNull(manifestProguardRules.singleFile().toPath(), userProguardRules)// 2.构建主dex集合 val mainDexClasses = mutableSetOf<String>()// 3. 获取系统平台的keep配置文件(参见
Android构建流程——篇七相关推荐
- Android构建流程——篇二
文章目录 预操作 任务列表 如何查看一个task类 Task1: checkDebugClasspath 1. input/output 2. 如何找到任务实现类 3. 核心类(AppClasspat ...
- Android构建流程——篇一
Android构建流程 前言 APK 构建流程 AGP(3.2.0)任务列表总览图 参考文献 前言 大家平时开发Android项目时一般都是点击AS run按钮,这样apk会自动安装到手机上,这整个过 ...
- Android构建流程——篇四
文章目录 Task9 generateDebugResValues 1. input/ouput 2. 核心类(GenerateResValues) Task10 generateDebugResou ...
- Android构建流程——篇六
文章目录 Task17: javaPreCompileDebug 1. input/ouput 2. 验证 3. 核心类(JavaPreCompileTask) Task18:compileDebug ...
- Android构建流程——篇五
文章目录 Task13: processDebugManifest 1. input/ouput 2. 整体流程 3. 调用链路 4. 核心类(MergeManifests) 5. AndroidBu ...
- Android构建流程——篇三
文章目录 Task5 checkDebugManifest 1. input/ouput 2. 核心类(CheckManifest) Task6 generateDebugBuildConfig 1. ...
- Android构建流程——篇八
文章目录 Task29 checkDebugLibraries 1. inut/ouput 2. 核心类(CheckMultiApkLibrariesTask) Task30 processDebug ...
- Android汽车服务篇(七) CarPowerManagementService
一. 前言 电源管理是AAOS上又一个比较特殊的部分. 由于车辆的使用场景的特殊性和复杂性, 同时需要和其他ECU(Electronic Control Unit)电子控制单元的配合, 都增加了车载 ...
- 优酷 Android 构建速度优化实践
作者:苏彦郊(木磊) Android 项目一般使用 gradle 作为构建打包工具,gradle 简洁.动态的功能特性为人津津乐道,同样,构建执行速度缓慢的缺陷也一直为人诟病. 近年来,随着优酷功能特 ...
最新文章
- MYSQL 使用自定义表变量
- ABB 机器人 IRBP系列转台的一段代码注释
- vue element menu侧边导航栏 数据渲染
- LeetCode刷题(22)
- python二维向量运算_python中二维数组的Elementwise与or或运算
- windows过滤中设备绑定的内核API之一
- 我是如何写作一本软件+哲学式的书籍的(上)
- 怎么用xmind整理我们获取的杂乱的信息
- 图书管理系统数据库设计
- 【Uniapp】Uniapp 实现 App 版本自动升级
- choco 使用详解
- 使用注册表清理右键新建菜单
- 玩转基因组浏览器之IGV展示bam文件
- ACdream 之ACfun 题解
- linux单片机用什么数据库,基于ARM-Linux的SQLite嵌入式数据库的研究 -单片机-电子工程世界网...
- 19、android面试题整理(自己给自己充充电吧)
- C++实现类似QT中的计时器QTime类(CQTime)
- 淘宝/天猫关键词搜索最新接口
- 燕教授使用体验,从漱口水到牙膏
- c++调用win32API控制打印机打印
热门文章
- python必刷面试_Python面试必刷题系列(5)
- kaggle机器学习作业(房价预测)
- AAAI 2022 | 条件局部图卷积网络用以气象预测
- EMNLP 2021 | 正则表达式与神经网络的深度融合(续)
- 生物岛实验室闵明玮课题组诚聘副研究员/博士后/科研助理/实习生
- 基于Conditional Layer Normalization的条件文本生成
- 细水长flow之f-VAEs:Glow与VAEs的联姻
- HDU1599 find the mincost route Floyd算法求最小环
- HDU1045 Fire Net 递归回溯
- python pip gpl_一文了解Python的pip工具