O segredo por trás da série de análise de código-fonte do Android Studio com um clique em Executar

Diretório de artigos

prefácio

1. O segredo por trás de clicar em Executar?

1.1 Tarefas do Gradle por trás do Run

1.2 Nome da tarefa

1.3 Descrição detalhada de algumas tarefas

2. A relação entre o plug-in Android Gradle e o AS

1. Análise do código-fonte principal do plug-in Android Gradle

3. Processo abrangente de construção do Android

3.1 A nova versão do processo de construção do Android

3.2 Versão antiga do processo de construção do Android

3.3: apto

3.4: complemento

3.5: Compilação do código-fonte Java

3.4: dex

3.6: apkbuilder

3.7:Assinante do Jar

3.8:zipalign

3.9 APK cria um processo mais detalhado

Análise de código-fonte de quatro AS

 4.1 Código principal

editar

4.2 Verifique o projeto e leia a configuração básica

4.3 Estatísticas de execução

4.4 Trabalho preparatório antes da compilação

4.5 Instalação e implantação

Cinco Resumo


prefácio

Palavras-chave: o segredo por trás da execução do Android Studio, processo de compilação do Android

提示:可能需要大概30分钟阅读时间

Antecedentes: Normalmente, no processo de desenvolvimento, a maioria das pessoas executa o aplicativo clicando no botão Executar, e o Android Studio será iniciado, e o código milagrosamente se tornará APK, que será instalado no celular e exibirá a interface do APP. O que está acontecendo nos bastidores?


1. O segredo por trás de clicar em Executar?

1.1 Tarefas do Gradle por trás do Run

Para resolver esse problema, você precisa entender o processo de construção do Gradle. Vejamos o processo de construção do Gradle.

O ciclo de vida do Gradle é dividido em três fases, nomeadamente Inicialização (fase de inicialização), Configuração (fase de configuração) e Execução (fase de execução), e a fase de execução executará uma série de tarefas para as principais obras de construção .

Então, naturalmente, o trabalho de construção por trás do botão Executar também é composto por uma série de tarefas, então temos uma maneira de verificar quais são essas tarefas? O Android Studio fornece uma função de registro poderosa, que requer principalmente as três etapas a seguir:

  1. 1.1 Clique em Exibir > Janelas de ferramentas > Construir e o processo de construção será exibido abaixo da interface do Android Studio ;     

Na figura acima, você pode ver que o processo geral após clicar em Construir é dividido em

1.1.1.1 Carregar construção

1.1.1.2 Construção de configuração 

1.1.1.3 Calcular gráfico de tarefa

1.1.1.4 executar tarefa

Então, vamos expandir o que foi dito acima e ver o que foi feito especificamente?

1.1.2 Expandido

Neste ponto, se clicarmos no botão Executar, vamos ver o que vai acontecer

 , por meio de observação, descobrimos que é preciso passar pelo processo de construção anterior e depois adicionar a instalação, ou seja, o Apk compilado é instalado no celular do usuário

1.2 Nome da tarefa

completed successfully	1 s 382 ms
Run build	981 ms
Load build	3 ms
Evaluate settings	2 ms
Finalize build cache configuration	
Configure build	122 ms
Load projects	2 ms
Calculate task graph	148 ms
Run tasks	598 ms
:app:buildInfoDebugLoader	9 ms
:app:preBuild	1 ms
:app:preDebugBuild	10 ms
:app:compileDebugAidl	
:app:checkDebugManifest	1 ms
:app:compileDebugRenderscript	
:app:generateDebugBuildConfig	2 ms
:app:prepareLintJar	
:app:generateDebugSources	
:app:javaPreCompileDebug	21 ms
:app:mainApkListPersistenceDebug	
:app:generateDebugResValues	3 ms
:app:generateDebugResources	
:app:mergeDebugResources	97 ms
:app:createDebugCompatibleScreenManifests	2 ms
:app:processDebugManifest	72 ms
:app:processDebugResources	25 ms
:app:compileDebugJavaWithJavac	27 ms
:app:instantRunMainApkResourcesDebug	1 ms
:app:mergeDebugShaders	2 ms
:app:compileDebugShaders	3 ms
:app:generateDebugAssets	
:app:mergeDebugAssets	3 ms
:app:validateSigningDebug	
:app:signingConfigWriterDebug	1 ms
:app:processInstantRunDebugResourcesApk	1 ms
:app:checkManifestChangesDebug	5 ms
:app:checkDebugDuplicateClasses	2 ms
:app:transformClassesWithExtractJarsForDebug	1 ms
:app:transformClassesWithInstantRunVerifierForDebug	26 ms
:app:transformClassesWithDependencyCheckerForDebug	9 ms
:app:mergeDebugJniLibFolders	1 ms
:app:processDebugJavaRes	
:app:transformNativeLibsWithMergeJniLibsForDebug	8 ms
:app:transformResourcesWithMergeJavaResForDebug	8 ms
:app:transformNativeLibsAndResourcesWithJavaResourcesVerifierForDebug	1 ms
:app:transformClassesWithInstantRunForDebug	24 ms
:app:transformClassesAndClassesEnhancedWithInstantReloadDexForDebug	14 ms
:app:incrementalDebugTasks	1 ms
:app:preColdswapDebug	1 ms
:app:fastDeployDebugExtractor	1 ms
:app:generateDebugInstantRunAppInfo	2 ms
:app:transformClassesWithDexBuilderForDebug	31 ms
:app:transformDexArchiveWithExternalLibsDexMergerForDebug	3 ms
:app:transformDexArchiveWithDexMergerForDebug	5 ms
:app:transformDexWithInstantRunDependenciesApkForDebug	2 ms
:app:instantRunSplitApkResourcesDebug	3 ms
:app:transformDexWithInstantRunSlicesApkForDebug	2 ms
:app:packageDebug	10 ms
:app:buildInfoGeneratorDebug	12 ms
:app:compileDebugSources	1 ms
:app:assembleDebug	

As tarefas acima podem ser divididas aproximadamente em cinco etapas:

  1. Preparação de dependências : Nesta fase, o gradle detecta se todas as bibliotecas das quais o módulo depende estão prontas . Caso este módulo dependa de outro módulo, o outro módulo também deverá ser compilado;
  2. Mesclando recursos e processando Manifesto : recursos de pacote e arquivos de Manifesto;
  3. Compilando : processando anotações do compilador, o código-fonte é compilado em bytecode;
  4. Pós-processamento (transformação ) : Todas as tarefas prefixadas com "transformação" são processadas nesta fase;
  5. Empacotamento e publicação : biblioteca gera arquivo .aar, aplicativo gera arquivo .apk

1.3 Descrição detalhada de algumas tarefas

  1. mergeDebugResources : descompacte todos os pacotes aar e mescle todos os arquivos de recursos em diretórios relevantes;
  2. processDebugManifest : mescla os nós em AndroidManifest.xml em todos os pacotes aar no AndroidManifest.xml do projeto
  3. processDebugResources :

a. Chame o aapt para gerar o projeto e todo o R.java do qual o aar depende b. Gere o arquivo de índice de recursos c. Tabela de símbolos de saída

  1. compileDebugJavaWithJavac : usado para compilar arquivos java em arquivos de classe

2. A relação entre o plug-in Android Gradle e o AS

1. Análise do código-fonte principal do plug-in Android Gradle

Com o entendimento preliminar acima, vamos dar uma olhada em como o Android Gradle Plugin implementa o processo de construção.

As classes correspondentes ao plugin apply: 'com.android.application' e apply plugin:'com.android.library' que usamos são AppPlugin e LibraryPlugin respectivamente. Todos eles herdam BasePlugin:

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);
    }
}

Fico muito feliz em ver que o método apply reescrito é definitivo, ou seja, só precisamos analisar a lógica no BasePlugin. O método principal é basePluginApply, o código-fonte é o seguinte:

private void basePluginApply(@NonNull Project project) {
      	//核心三个方法
				configureProject();
      	configureExtension();
				createTasks();
    }

configurarProjeto()

    private void configureProject() {
   			//主构建器类,它提供了处理构建的所有数据,比如 DefaultProductFlavor、DefaultBuildType 以及一些依赖项,在执行的时候使用它们特定的构建步骤
        AndroidBuilder androidBuilder = new AndroidBuilder();
				//...
        // 依赖 Java 插件
        project.getPlugins().apply(JavaBasePlugin.class);
				//Plugin 的全局变量
        new GlobalScope()
    }

configureExtension()

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);
    }

criarTask()

private void createTasks() {
      	//注册一些默认 Task,比如 uninstallAll、deviceCheck、connectedCheck 等
        taskManager.createTasksBeforeEvaluate()

        createAndroidTasks();
    }

    final void createAndroidTasks() {
      	//...
        List<VariantScope> variantScopes = variantManager.createAndroidTasks();
    }

Depois vai até o método TaskManager#createTasksForVariantScope, que é um método abstrato, vamos dar uma olhada em sua implementação no ApplicationTaskManager:

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);
    }


3. Processo abrangente de construção do Android

3.1 A nova versão do processo de construção do Android

3.2 Versão antiga do processo de construção do Android

No fluxograma acima, posso ver que o processo de construção do APK pode ser dividido nas sete etapas a seguir:

  1. Empacote arquivos de recursos res por meio do aapt para gerar arquivos R.java, resources.arsc e res (binários e não binários, como res/raw e pic permanecem como estão)
  2. Processe o arquivo .aidl e gere o arquivo de interface Java correspondente
  3. Compile R.java, arquivos de interface Java e arquivos de origem Java por meio do Java Compiler para gerar arquivos .class
  4. Use a ferramenta dx/d8 para processar arquivos .class e arquivos .class em bibliotecas de terceiros para gerar classes.dex
  5. Através da ferramenta apkbuilder, empacote o resources.arsc gerado pelo aapt junto com o arquivo res, arquivo assets e classes.dex para gerar o apk
  6. Use a ferramenta Jarsigner para depurar ou liberar o sinal do apk acima
  7. Use a ferramenta zipalign para alinhar o apk assinado
  8. Isso foi verificado novamente através do processo acima. Se quisermos aumentar a velocidade de construção do Apk, use o pacote AAR ou o jar já empacotado para evitar a compilação secundária, reduzindo assim o tempo de compilação. Este é um ponto de otimização.

3.3: apto

aapt (Android Asset Packaging Tool), usado para empacotar arquivos de recursos, gerar R.java e recursos compilados. O código fonte do aapt está localizado no diretório frameworks/base/tools/aapt2.

3.4: complemento

Usado para processar arquivos aidl, o código fonte está localizado em frameworks/base/tools/aidl. Insira o arquivo com o sufixo aidl e produza o código Java do lado C/S que pode ser usado para comunicação do processo, localizado em build/generated/source/aidl.

3.5: Compilação do código-fonte Java

Com os arquivos Java gerados por R.java e aidl, além do código-fonte do projeto, agora você pode usar javac para realizar a compilação normal de java para gerar arquivos de classe.

Entrada: pasta de origem java (também inclui: R.java em build/gerado, arquivos Java gerados por aidl e BuildConfig.java)

Saída: para compilação gradle, você pode ver o arquivo de classe de saída em build/intermediates/classes.

3.4: dex

Use a ferramenta dx/d8 para converter arquivos de classe em arquivos dex, gerar pools constantes, eliminar dados redundantes, etc.

3.6: apkbuilder

O arquivo APK é empacotado e gerado. Agora ele foi empacotado pela classe ApkBuilder de sdklib.jar. A entrada é o arquivo .ap_ contendo resources.arsc que geramos antes, o arquivo dex gerado na etapa anterior e outros recursos, como como pacotes jni e jar. H.

3.7:Assinante do Jar

Assine o arquivo apk, necessário para que o APK seja instalado no dispositivo. Agora não apenas o jarsigner, mas também o apksigner podem ser usados.

3.8:zipalign

Alinhe o arquivo apk assinado para que todos os arquivos de recursos no apk sejam um múltiplo inteiro de quatro bytes desde o início do arquivo, para que seja mais rápido acessar o arquivo apk por meio do mapeamento de memória.

Deve-se notar que o uso de diferentes ferramentas de sinalização tem impacto no alinhamento. Se você usar o jarsigner, só poderá executar o zipalign depois que o arquivo APK for assinado; se você usar o apksigner, só poderá executar o zipalign antes do arquivo APK ser assinado

3.9 APK cria um processo mais detalhado

Fonte: https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6MzVmYmFlN2FhYjkzMzc2Ng

Análise de código-fonte de quatro AS

 4.1 Código principal

O código-fonte da versão AS3.4.0 é analisado aqui. A compilação de todo o código-fonte do AS é bastante complicada e demorada. Publicarei um tutorial especial sobre como compilar o AS posteriormente.

4.2 Verifique o projeto e leia a configuração básica

AndroidRunConfigurationBase -> getState

 

O método principal é doGetState() e, em seguida, veremos o código-fonte desse método. Como esse método é relativamente grande, veremos apenas uma parte dele

Selecione aqui o dispositivo para implantação e instalação

4.3 Estatísticas de execução

IDEA permite realizar algumas tarefas antes da execução, por exemplo, é necessário compilar um projeto Java antes de executá-lo. Nosso projeto Android é semelhante, antes da instalação e implantação, você precisa compilar e empacotar. Este processo é denominado: Antes do lançamento.

 Pode ser configurado ao compilar o aplicativo

 AS nos fornece Make com reconhecimento de Gradle por padrão. O código fonte correspondente a Before launch é: BeforeRunBuilder

public interface BeforeRunBuilder {
  boolean build(@NotNull GradleTaskRunner taskRunner, @NotNull List<String> commandLineArguments) throws InterruptedException,
                                                                                                         InvocationTargetException;
}

O código a seguir executa essencialmente tarefas Gradle. No ambiente de depuração, o padrão é assembleDebug. Se o usuário alterar as variantes de construção, elas serão alteradas de acordo, conforme mostrado na figura abaixo

4.4 Trabalho preparatório antes da compilação

Você pode ver isso no método createBuilder()

 DefaultGradleBuilder está aqui para montar o comando de compilação gradle, por exemplo:

组装 Gradle task:gradle[:"assemble" + compileType]

 No final, GradleBuildInvoker será chamado através do método de reflexão. Este objeto chama principalmente o método gradle diretamente e, em seguida, exibe o processo de execução na mensagem do painel AS e, ao mesmo tempo, exibe-o no console Gradle. Isto é onde AS e Gradle realmente se combinam. A seguir, vamos dar uma olhada nos métodos principais específicos deste objeto.

4.5 Instalação e implantação

Após a conclusão da compilação, ela retornará à fase de execução do RunState. Esta fase deve ser chamada de implantação. O código principal está em

Método AndroidRunState--execute()

Neste momento, o objeto LaunchTaskRunner será chamado, por ser um Runable, seu método run será chamado a seguir.

Ao analisar o código-fonte, podemos ver a lógica relacionada ao InstantRun, julgamento de versão, julgamento de dispositivo, log de saída, chamada de comando pm para instalar APK, evocar a primeira tela, etc.

五 总结

 Pela análise acima, acredito que todos já entenderam todo o processo de compilação do apk do AS, podendo haver alguns detalhes que não estão muito claros, então você mesmo pode ler o código, é necessário conhecer claramente o objeto RunStats da qual o AS compila o apk depende. Esta é uma máquina de estado de controle de processo muito importante quando o AS está compilando. Ao mesmo tempo, é necessário entender a combinação de AS e gradle nesse estágio. Após a combinação dos dois ser concluída, O resto do trabalho é entregue ao Gradle. Depois que o Gradle terminar seu trabalho, ele notificará o AS para concluir a instalação e implantação final.

Acho que você gosta

Origin blog.csdn.net/qq_18757557/article/details/130481880
Recomendado
Clasificación