O segundo Visualização do Desenvolvedor do Android 11 já está disponível, teste e compartilhe seu feedback.

Métodos para ativar multidex para apps com mais de 64 K

Quando seu app e as bibliotecas a que ele faz referência excederem 65.536 métodos, ocorrerá um erro de compilação que indica que seu app atingiu o limite da arquitetura de compilação do Android:

    trouble writing output:
    Too many field references: 131000; max is 65536.
    You may try using --multi-dex option.
    

As versões mais antigas do sistema de compilação relatam um erro diferente, que indica o mesmo problema:

    Conversion to Dalvik format failed:
    Unable to execute dex: method ID not in [0, 0xffff]: 65536
    

Ambas as condições de erro exibem um número em comum: 65.536. Esse número representa o total de referências que podem ser invocadas pelo código em um único arquivo de bytecode Dalvik Executable (DEX). Esta página explica como superar essa limitação, ativando uma configuração de app denominada multidex, que permite que o app compile e leia vários arquivos DEX.

Sobre o limite de 64 K referências

Arquivos de app para Android (APK) contêm arquivos de bytecode executáveis no formato Dalvik Executable (DEX), que contêm o código compilado usado para executar o app. A especificação do executável Dalvik limita o total de métodos que podem ser referenciados em um único arquivo DEX a 65.536. Isso inclui métodos de framework do Android, métodos de biblioteca e métodos do seu próprio código. No contexto da ciência da computação, o termo Kilo, K, denota 1.024 (ou 2^10). Como 65.536 equivale a 64 X 1.024, esse limite é chamado de "limite de 64 K referências".

Compatibilidade com multidex antes do Android 5.0

Versões da plataforma anteriores ao Android 5.0 (API nível 21) usam o tempo de execução da Dalvik para executar o código do app. Por padrão, a Dalvik limita os aplicativos a um único arquivo de bytecode classes.dex por APK. Para contornar essa limitação, você pode adicionar a biblioteca de suporte multidex ao seu projeto:

    dependencies {
        def multidex_version = "2.0.1"
        implementation 'androidx.multidex:multidex:$multidex_version'
    }
       

Para visualizar as versões atuais desta biblioteca, consulte as informações de Multidex na página de versões.

Se você não estiver usando o AndroidX, adicione a seguinte dependência de biblioteca de suporte:

    dependencies {
      implementation 'com.android.support:multidex:1.0.3'
    }
    

Essa biblioteca se torna parte do arquivo DEX principal do app e, em seguida, gerencia o acesso aos outros arquivos DEX e ao código que eles contêm. Saiba mais detalhes na seção abaixo sobre como configurar seu app para multidex.

Compatibilidade com multidex para Android 5.0 e versões posteriores

O Android 5.0 (API de nível 21) e versões mais recentes usam um tempo de execução chamado ART, que oferece compatibilidade nativa com o carregamento de vários arquivos DEX a partir de arquivos APK. O ART executa a pré-compilação no momento da instalação do app, que verifica a presença de arquivos classesN.dex e os compila em um único arquivo .oat para execução no dispositivo Android. Portanto, se sua minSdkVersion for 21 ou mais recente, o multidex é ativado por padrão e a biblioteca de suporte multidex não será necessária.

Para saber mais sobre o tempo de execução do Android 5.0, leia ART e Dalvik.

Observação: ao executar seu aplicativo usando o Android Studio, a versão é otimizada para os dispositivos de destino para a que você implanta. Isso inclui a ativação de multidex quando os dispositivos de destino estiverem executando o Android 5.0 e versões posteriores. Como essa otimização é aplicada somente ao implantar seu aplicativo usando o Android Studio, talvez seja necessário configurar sua versão de lançamento para multidex para evitar o limite de 64K.

Evitar o limite de 64 K

Antes de configurar seu app para permitir o uso de 64 K ou mais referências a métodos, reduza o número total de referências chamadas pelo código do seu app, incluindo métodos definidos pelo código do app ou pelas bibliotecas incluídas. As seguintes estratégias podem ajudar a não atingir o limite de referências do DEX:

  • Verifique as dependências diretas e transitivas do app: todas as dependências de bibliotecas grandes incluídas no app precisam ser usadas de forma a compensar a quantidade de código adicionada ao app. Um antipadrão comum é incluir uma biblioteca muito grande, porque alguns métodos utilitários foram úteis. Muitas vezes, a redução das dependências de código do app pode ajudar a evitar o limite de referências do DEX.
  • Remover código não usado com o R8: ative a redução de código para executar o R8 nas versões de lançamento. A redução ativada garante que você não inclua código não utilizado nos APKs.

O uso dessas técnicas pode ajudar a evitar a necessidade de ativar multidex no app, além de diminuir o tamanho geral do APK.

Configurar o app para multidex

Se sua minSdkVersion estiver configurada para 21 ou mais recente, o multidex será ativado por padrão e a biblioteca de suporte multidex não será necessária.

No entanto, se sua minSdkVersion estiver definida como 20 ou anterior, será necessário usar a biblioteca de suporte multidex e fazer as seguintes modificações no seu projeto de aplicativo:

  1. Modifique o arquivo build.gradle de nível de módulo para ativar o multidex e adicionar a biblioteca multidex como dependência, como mostrado a seguir:

        android {
            defaultConfig {
                ...
                minSdkVersion 15
                targetSdkVersion 28
                multiDexEnabled true
            }
            ...
        }
    
        dependencies {
          implementation 'com.android.support:multidex:1.0.3'
        }
        
  2. Dependendo de você modificar ou não a classe Application, execute uma das seguintes ações:
    • Se você não modificou a classe Application, edite o arquivo de manifesto para definir android:name na tag <application> desta forma:

          <?xml version="1.0" encoding="utf-8"?>
          <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.example.myapp">
              <application
                      android:name="android.support.multidex.MultiDexApplication" >
                  ...
              </application>
          </manifest>
          
    • Se você modificou a classe Application, mude-a para estender MultiDexApplication (se possível) da seguinte forma:

      Kotlin

          class MyApplication : MultiDexApplication() {...}
          

      Java

          public class MyApplication extends MultiDexApplication { ... }
          
    • Ou, se você modificou a classe Application, mas não foi possível mudar a classe base, modifique o método attachBaseContext() e chame MultiDex.install(this) para ativar o multidex:

      Kotlin

          class MyApplication : SomeOtherApplication() {
      
              override fun attachBaseContext(base: Context) {
                  super.attachBaseContext(base)
                  MultiDex.install(this)
              }
          }
          

      Java

          public class MyApplication extends SomeOtherApplication {
            @Override
            protected void attachBaseContext(Context base) {
               super.attachBaseContext(base);
               MultiDex.install(this);
            }
          }
          

      Cuidado: não execute MultiDex.install() ou qualquer outro código por meio de reflexão ou JNI antes da conclusão de MultiDex.install(). O rastreio multidex não seguirá essas chamadas, causando ClassNotFoundException ou erros de verificação devido a uma partição de classe inválida entre arquivos DEX.

Agora, ao compilar o app, as ferramentas de compilação do Android construirão um arquivo DEX principal (classes.dex) e arquivos de apoio DEX (classes2.dex, classes3.dex e assim por diante), conforme necessário. Em seguida, o sistema de compilação empacotará todos os arquivos DEX no APK.

No tempo de execução, as APIs do multidex usam um carregador de classes especial para pesquisar todos os arquivos DEX disponíveis para os métodos (em vez de pesquisar apenas o arquivo principal classes.dex).

Limitações de biblioteca de suporte a multidex

A biblioteca de suporte a multidex tem algumas limitações que você precisa conhecer e testar ao incorporá-la na configuração de compilação do seu app:

  • A instalação de arquivos DEX durante a inicialização na partição de dados de um dispositivo é complexa e poderá resultar em erros "O app não está respondendo" (ANR, na sigla em inglês) se os arquivos DEX secundários forem grandes. Para evitar esse problema, ative a redução de código para diminuir o tamanho dos arquivos DEX e remover as partes não utilizadas do código.
  • Quando executado em versões anteriores ao Android 5.0 (API nível 21), o uso de multidex não é suficiente para contornar o limite linearAlloc (problema 78035). Esse limite foi aumentado no Android 4.0 (API nível 14), mas isso não resolveu o problema completamente. E em versões anteriores ao Android 4.0, é possível atingir o limite linearAlloc antes de atingir o limite de índice do DEX. Portanto, se você estiver segmentando níveis de API inferiores a 14, faça um teste completo nessas versões da plataforma, uma vez que o app poderá ter problemas na inicialização ou quando determinados grupos de classes forem carregados.

    A redução de código pode diminuir ou possivelmente eliminar esses problemas.

Declarar as classes necessárias no arquivo DEX principal

Ao criar cada arquivo DEX de um app multidex, as ferramentas de compilação tomam decisões complexas para determinar as classes necessárias no arquivo DEX principal, para que o app seja iniciado corretamente. Se alguma classe necessária para a inicialização não for fornecida no arquivo DEX principal, o app falhará apresentando o erro java.lang.NoClassDefFoundError.

Isso não pode ocorrer com código acessado diretamente no código do app, porque as ferramentas de compilação reconhecem esses caminhos de código. Mas é possível que isso ocorra quando os caminhos do código são menos visíveis, como quando uma biblioteca usada tem dependências complexas. Por exemplo, se o código utiliza introspecção ou invocação de métodos Java no código nativo, essas classes talvez não sejam reconhecidas como necessárias no arquivo DEX principal.

Portanto, se você receber java.lang.NoClassDefFoundError, será necessário especificar manualmente essas outras classes no arquivo DEX principal, declarando-as com multiDexKeepFile ou com a propriedade multiDexKeepProguard no seu tipo de compilação. Se uma classe for correspondida no arquivo multiDexKeepFile ou multiDexKeepProguard, ela será adicionada ao arquivo DEX principal.

Propriedade multiDexKeepFile

O arquivo especificado em multiDexKeepFile precisa conter uma classe por linha no formato com/example/MyClass.class. Por exemplo, você pode criar um arquivo chamado multidex-config.txt como este:

    com/example/MyClass.class
    com/example/MyOtherClass.class
    

Em seguida, você pode declarar esse arquivo para um tipo de compilação da seguinte forma:

    android {
        buildTypes {
            release {
                multiDexKeepFile file('multidex-config.txt')
                ...
            }
        }
    }
    

Lembre-se de que o Gradle lê caminhos relativos ao arquivo build.gradle. Portanto, o exemplo acima funcionará se multidex-config.txt estiver no mesmo diretório do arquivo .

Propriedade multiDexKeepProguard

O arquivo multiDexKeepProguard usa o mesmo formato do Proguard e é compatível com toda a gramática do Proguard. Para mais informações sobre o formato e a gramática do Proguard, consulte a seção Opções do Keep (link em inglês) no manual do Proguard.

O arquivo que você especificar em multiDexKeepProguard precisará conter opções de -keep em qualquer sintaxe válida do ProGuard. Por exemplo, -keep com.example.MyClass.class. Você pode criar um arquivo chamado multidex-config.pro como este:

    -keep class com.example.MyClass
    -keep class com.example.MyClassToo
    

Ou, se você quiser especificar todas as classes de um pacote, o arquivo será assim:

    -keep class com.example.** { *; } // All classes in the com.example package
    

Em seguida, você pode declarar esse arquivo para um tipo de compilação da seguinte forma:

    android {
        buildTypes {
            release {
                multiDexKeepProguard file('multidex-config.pro')
                ...
            }
        }
    }
    

Otimizar o multidex em compilações de desenvolvimento

Configurações de multidex exigem um tempo de processamento de compilação significativamente maior porque o sistema de compilação precisa tomar decisões complexas sobre quais classes devem ser incluídas no arquivo DEX principal e quais delas podem ser incluídas nos arquivos DEX secundários. Isso significa que compilações incrementais que utilizam multidex costumam ser mais demoradas e podem retardar o processo de desenvolvimento.

Para reduzir os tempos mais longos da compilação incremental, você deve usar pré-dexação para reutilizar a saída multidex entre compilações. A pré-dexação utiliza um formato ART disponível apenas no Android 5.0 (API nível 21) ou posterior. Se você usa o Android Studio 2.3 ou mais recente, o ambiente de desenvolvimento integrado (IDE, na sigla em inglês) utiliza esse recurso automaticamente ao implantar seu app em um dispositivo com Android 5.0 (API nível 21) ou mais recente.

Dica: o Plug-in do Android para Gradle 3.0.0 e versões mais recentes incluem outras melhorias para otimizar as velocidades de compilação, como a dexação por classe (para que apenas as classes modificadas sejam redexadas). Em geral, para atingir a melhor experiência de desenvolvimento, atualize sempre para a versão mais recente do Android Studio e do plug-in do Android.

No entanto, se você estiver executando as compilações de Gradle a partir da linha de comando, será necessário definir o minSdkVersion como 21 ou mais recente para ativar a pré-dexação. Uma estratégia útil para preservar as configurações da compilação de produção é criar duas versões do seu app usando variações de produtos: uma variação de desenvolvimento e uma variação de lançamento com valores diferentes para minSdkVersion, como mostrado abaixo.

    android {
        defaultConfig {
            ...
            multiDexEnabled true
            // The default minimum API level you want to support.
            minSdkVersion 15
        }
        productFlavors {
            // Includes settings you want to keep only while developing your app.
            dev {
                // Enables pre-dexing for command line builds. When using
                // Android Studio 2.3 or higher, the IDE enables pre-dexing
                // when deploying your app to a device running Android 5.0
                // (API level 21) or higher—regardless of what you set for
                // minSdkVersion.
                minSdkVersion 21
            }
            prod {
                // If you've configured the defaultConfig block for the production version of
                // your app, you can leave this block empty and Gradle uses configurations in
                // the defaultConfig block instead. You still need to include this flavor.
                // Otherwise, all variants use the "dev" flavor configurations.
            }
        }
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                     'proguard-rules.pro'
            }
        }
    }
    dependencies {
        implementation 'com.android.support:multidex:1.0.3'
    }
    

Para aprender outras estratégias para melhorar as velocidades de compilação (seja no Android Studio ou na linha de comando), leia Otimizar a velocidade de compilação. Para saber mais sobre o uso de variantes de compilação, consulte Configurar variantes de compilação.

Dica: agora que você tem variantes de versão diferentes para necessidades multidex diferentes, também pode fornecer um arquivo de manifesto diferente para cada variante (para que apenas o manifesto para a API de nível 20 e anteriores mudem o nome da tag <application>) ou criar uma Application subclasse para cada variante (para que somente a API de nível 20 e anteriores estenda a classe MultiDexApplication ou chame MultiDex.install(this)).

Testar apps multidex

Ao escrever testes de instrumentação para apps multidex, nenhuma outra configuração será necessária se você usar uma instrumentação MonitoringInstrumentation (ou AndroidJUnitRunner). Se você usar outra Instrumentation, modifique o método onCreate() dela com o seguinte código:

Kotlin

    fun onCreate(arguments: Bundle) {
      MultiDex.install(targetContext)
      super.onCreate(arguments)
      ...
    }
    

Java

    public void onCreate(Bundle arguments) {
      MultiDex.install(getTargetContext());
      super.onCreate(arguments);
      ...
    }