Pergunta

Como você detecta programaticamente o tamanho da pilha de aplicativos disponível para um aplicativo Android?

Ouvi dizer que há uma função que faz isso em versões posteriores do SDK. De qualquer forma, estou procurando uma solução que funcione por 1,5 ou mais.

Foi útil?

Solução

Existem duas maneiras de pensar na sua frase "tamanho da pilha de aplicativos disponível":

  1. Quanto heap meu aplicativo pode usar antes que um erro difícil seja acionado? E

  2. Quanto heap deve Meu uso de aplicativo, dadas as restrições da versão e hardware do sistema operacional do Android do dispositivo do usuário?

Existe um método diferente para determinar cada um dos itens acima.

Para o item 1 acima: maxMemory()

que pode ser invocado (por exemplo, em sua atividade principal onCreate() método) da seguinte maneira:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Este método diz a você quantos tota bytes de pilha do seu aplicativo é permitido usar.

Para o item 2 acima: getMemoryClass()

que pode ser chamado da seguinte forma:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Este método diz a você aproximadamente quantos megabytes de empunhar seu aplicativo deve Use se ele quiser ser respeitoso com os limites do presente dispositivo e dos direitos de outros aplicativos de correr sem serem repetidamente forçados ao onStop() / onResume() Cicione, pois eles são rudemente fora da memória, enquanto seu aplicativo de elefante toma banho no Android Jacuzzi.

Essa distinção não está claramente documentada, até onde eu sei, mas testei essa hipótese em cinco dispositivos Android diferentes (veja abaixo) e confirmei para minha própria satisfação que essa é uma interpretação correta.

Para uma versão de estoque do Android, maxMemory() normalmente retornará sobre o mesmo número de megabytes que são indicados em getMemoryClass() (ou seja, aproximadamente um milhão de vezes o último valor).

A única situação (da qual estou ciente) para a qual os dois métodos podem divergir está em um dispositivo enraizado que executa uma versão Android como o CyanogenMod, que permite ao usuário Selecione Qual o tamanho do tamanho de um pilhão para cada aplicativo. Em CM, por exemplo, essa opção aparece em "Configurações do CyanogenMod" / "Performance" / "VM Heap Size".

Nota: Esteja ciente de que a definição desse valor manualmente pode atrapalhar seu sistema, especialmente se você selecionar um valor menor do que o normal para o seu dispositivo.

Aqui estão meus resultados de teste mostrando os valores retornados por maxMemory() e getMemoryClass() Para quatro dispositivos diferentes, executando cianogenmod, usando dois valores diferentes de heap (manualmente no conjunto) para cada um:

  • G1:
    • Com o tamanho da pilha VM definido como 16MB:
      • MaxMemory: 16777216
      • getMemoryclass: 16
    • Com o tamanho da pilha VM definido como 24 MB:
      • MaxMemory: 25165824
      • getMemoryclass: 16
  • Moto Droid:
    • Com o tamanho da pilha VM definido como 24 MB:
      • MaxMemory: 25165824
      • getMemoryClass: 24
    • Com o tamanho da pilha VM definido como 16MB:
      • MaxMemory: 16777216
      • getMemoryClass: 24
  • Nexus um:
    • Com o tamanho da pilha VM definido como 32MB:
      • MaxMemory: 33554432
      • getMemoryClass: 32
    • Com o tamanho da pilha VM definido como 24 MB:
      • MaxMemory: 25165824
      • getMemoryClass: 32
  • ViewSonic GTAB:
    • Com o tamanho da pilha VM definido como 32:
      • MaxMemory: 33554432
      • getMemoryClass: 32
    • Com o tamanho da pilha VM definido para 64:
      • MaxMemory: 67108864
      • getMemoryClass: 32

Além do exposto, testei em um Novo7 Paladin Tablet que executa o sanduíche de sorvete. Esta era essencialmente uma versão de estoque do ICS, exceto que eu enraizei o tablet através de um processo simples que não substitui o sistema operacional inteiro e, em particular, não fornece uma interface que permitiria que o tamanho da pilha fosse ajustado manualmente.

Para esse dispositivo, aqui estão os resultados:

  • Novo7
    • MaxMemory: 62914560
    • getMemoryclass: 60

Também (por Kishore em um comentário abaixo):

  • Htc um x
    • MaxMemory: 67108864
    • getMemoryclass: 64

E (por comentário de Akapuppi):

  • Samsung Galaxy Core Plus
    • MaxMemory: (não especificado em comentário)
    • getMemoryclass: 48
    • LargememoryClass: 128

Por um comentário de cmcromance:

  • Galaxy S3 (Jelly Bean) Heap
    • MaxMemory: 268435456
    • getMemoryclass: 64

E (por comentários de Tencent):

  • LG Nexus 5 (4.4.3) Normal
    • MaxMemory: 201326592
    • getMemoryclass: 192
  • LG Nexus 5 (4.4.3) Heap grande
    • MaxMemory: 536870912
    • getMemoryclass: 192
  • Galaxy Nexus (4.3) Normal
    • MaxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) Heap grande
    • MaxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) Normal
    • MaxMemory: 201326592
    • getMemoryclass: 192
  • Galaxy S4 Play Store Edition (4.4.2) Heap grande
    • MaxMemory: 536870912
    • getMemoryclass: 192

Outros dispositivos

  • Huawei Nexus 6p (6.0.1) Normal
    • MaxMemory: 201326592
    • getMemoryclass: 192

Não testei esses dois métodos usando a opção Manifesto Android especial: Largeheap = "True" disponível desde o favo de mel, mas graças ao CMCromance e ao Tencent, temos alguns valores de amostra de LargeSheap, conforme relatado acima.

Meu expectativa (que parece ser suportado pelos números grandes do Haap acima) seria que essa opção teria um efeito semelhante a definir a pilha manualmente por meio de um sistema operacional enraizado - ou seja, aumentaria o valor de maxMemory() enquanto sai getMemoryClass() sozinho. Existe outro método, getLargememoryclass (), que indica quanta memória é permitida para um aplicativo usando a configuração Largeheap. A documentação para getLargememoryclass () afirma: "A maioria dos aplicativos não deve precisar dessa quantidade de memória e, em vez disso, deve permanecer no limite getMemoryClass ()".

Se eu adivinhei corretamente, o uso dessa opção teria os mesmos benefícios (e perigos) que usaria o espaço disponibilizado por um usuário que aumentou a pilha por meio de um sistema operacional enraizado (ou seja, se o seu aplicativo usar a memória adicional, Provavelmente não jogará tão bem com quaisquer outros aplicativos que o usuário esteja em execução ao mesmo tempo).

Observe que a classe de memória aparentemente não precisa ser um múltiplo de 8 MB.

Podemos ver do exposto que o getMemoryClass() O resultado é imutável para uma determinada configuração de dispositivo/SO, enquanto o valor maxmemory () altera quando a pilha é definida de maneira diferente pelo usuário.

Minha própria experiência prática é que, no G1 (que tem uma classe de memória de 16), se eu selecionar manualmente 24 MB como tamanho da pilha, posso funcionar sem errar mesmo quando meu uso de memória é permitido para 20 MB (presumivelmente vá até 24 MB, embora eu não tenha tentado isso). Mas outros aplicativos igualmente grandes podem ser liberados da memória como resultado do porquinho do meu próprio aplicativo. E, inversamente, minha O aplicativo pode ser liberado da memória se esses outros aplicativos de alta manutenção forem levados para o primeiro plano pelo usuário.

Portanto, você não pode examinar a quantidade de memória especificada por maxMemory(). E você deveria tentar permanecer dentro dos limites especificados por getMemoryClass(). Uma maneira de fazer isso, se tudo mais falhar, pode ser limitar a funcionalidade para esses dispositivos de uma maneira que conserva a memória.

Finalmente, se você planeja examinar o número de megabytes especificados em getMemoryClass(), meu conselho seria trabalhar muito e duro para salvar e restaurar o estado do seu aplicativo, para que a experiência do usuário seja praticamente ininterrupta se um onStop() / onResume() Ciclo ocorre.

No meu caso, por razões de desempenho, estou limitando meu aplicativo a dispositivos executando 2.2 e acima, e isso significa que quase todos os dispositivos que executam meu aplicativo terão uma classe de memória de 24 ou mais. Para que eu possa projetar para ocupar até 20 MB de pilha e me sentir bastante confiante de que meu aplicativo será bom com os outros aplicativos que o usuário pode estar em execução ao mesmo tempo.

Mas sempre haverá alguns usuários enraizados que carregaram uma versão 2.2 ou acima do Android em um dispositivo mais antigo (por exemplo, um G1). Quando você encontra essa configuração, idealmente, você deve analisar seu uso da memória, mesmo que maxMemory() está lhe dizendo que você pode ir muito mais alto do que os 16 MB que getMemoryClass() está lhe dizendo que você deve estar direcionado. E se você não puder garantir que seu aplicativo viva nesse orçamento, pelo menos verifique se isso onStop() / onResume() funciona perfeitamente.

getMemoryClass(), conforme indicado por Diane Hackborn (hackbod) acima, está disponível apenas de volta ao nível 5 da API (Android 2.0) e, assim, como ela aconselha, você pode assumir que o hardware físico de qualquer dispositivo executando uma versão anterior do sistema operacional foi projetado Para suportar os aplicativos ideais que ocupem um espaço de monte de não mais que 16 MB.

Por contraste, maxMemory(), de acordo com a documentação, está disponível desde o nível 1 da API. maxMemory(), em uma versão pré-2.0, provavelmente retornará um valor de 16 MB, mas eu Faz Veja que nas minhas versões (muito mais tarde) de cianogenmod, o usuário pode selecionar um valor de pilha tão baixo quanto 12 MB, o que presumivelmente resultaria em um limite mais baixo de heap, e por isso sugiro que você continuaria testando o maxMemory() valor, mesmo para versões do sistema operacional antes de 2.0. Você pode até se recusar a executar no evento improvável de que esse valor seja definido ainda mais que 16 MB, se você precisar ter mais do que maxMemory() indica é permitido.

Outras dicas

O oficial API é:

Isso foi introduzido em 2.0, onde apareceram dispositivos de memória maiores. Você pode assumir que os dispositivos executando versões anteriores do sistema operacional estão usando a classe de memória original (16).

Debug.getNativeHeapSize() Vou fazer o truque, eu deveria pensar. Está lá desde 1,0, no entanto.

o Debug A classe tem muitos ótimos métodos para rastrear alocações e outras preocupações de desempenho. Além disso, se você precisar detectar uma situação de baixa memória, confira Activity.onLowMemory().

Veja como você faz isso:

Obtendo o tamanho máximo da pilha que o aplicativo pode usar:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Conseguindo quanto da pilha seu aplicativo usa atualmente:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Conseguindo quanto da pilha seu aplicativo pode usar agora (memória disponível):

long availableMemory=maxMemory-usedMemory;

E, para formatar cada um deles, você pode usar:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 

Isso retorna o tamanho da pilha máxima em bytes:

Runtime.getRuntime().maxMemory()

Eu estava usando o ActivityManager.getMemoryClass (), mas no CyanogenMod 7 (não o testei em outro lugar), ele retornará valor errado se o usuário definir o tamanho da pilha manualmente.

Algumas operações são mais rápidas que o Java Heap Space Manager. Operações de atraso Por algum tempo, o espaço de memória livre pode liberar. Você pode usar este método para escapar do erro do tamanho da pilha:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}

Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 maxmemory () = 201326592

Cometi o erro de prototipar meu jogo no Nexus 7 e depois descobrir que ele ficou sem memória quase imediatamente no tablet genérico 4.04 genérico da minha esposa (MemoryClass 48, MaxMemory 50331648)

Vou precisar reestruturar meu projeto para carregar menos recursos quando determinar que o MemoryClass estiver baixo.
Existe uma maneira de Java para ver o tamanho atual da pilha? (Eu posso vê -lo claramente no logcat ao depurar, mas eu gostaria de ver isso no código para se adaptar, como se CurrentHeap> (maxmemory/2) descarregar bitmaps de alta qualidade carregar baixa qualidade

Você quer dizer programaticamente, ou apenas enquanto está desenvolvendo e depurando? Se o último, você pode ver essas informações da perspectiva do DDMS no Eclipse. Quando o seu emulador (possivelmente até o telefone físico que está conectado) estiver em execução, ele listará os processos ativos em uma janela à esquerda. Você pode selecioná -lo e há uma opção para rastrear as alocações de heap.

Runtime rt = Runtime.getRuntime();
rt.maxMemory()

O valor é b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

O valor é mb

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top