O aplicativo reinicia em vez de continuar

194

Espero que alguém possa me ajudar a descobrir, se não uma solução, pelo menos uma explicação para um comportamento.

O problema:

Em alguns dispositivos, pressionar o ícone do iniciador faz com que a tarefa atual seja reiniciada; em outros, a intenção inicial de inicialização é acionada (reiniciando efetivamente o aplicativo). Por que isso acontece?

O detalhe:

Quando você pressiona o "Ícone do iniciador", o aplicativo inicia normalmente - ou seja, suponho que um Intent seja iniciado com o nome do seu primeiro Activitycom a ação android.intent.action.MAINe a categoria android.intent.category.LAUNCHER. No entanto, esse nem sempre pode ser o caso:

Na maioria dos dispositivos, se você pressionar o ícone do iniciador após o aplicativo já estar em execução, a Atividade atualmente em execução nesse processo será retomada ( NÃO a inicial Activity). Ele é retomado da mesma maneira como se você o tivesse selecionado em "Tarefas recentes" no menu do SO. Esse é o comportamento que eu quero em todos os dispositivos.

No entanto, em outros dispositivos selecionados, ocorre um comportamento diferente:

  • No Motorola Xoom, quando você pressiona o ícone do iniciador, o aplicativo sempre inicia o lançamento inicial, Activityindependentemente do que está em execução no momento. Suponho que os ícones do iniciador sempre iniciem a intenção "LAUNCHER".

  • Na guia 2 da Samsung, quando você pressiona o ícone do iniciador, se você acabou de instalar o aplicativo, ele sempre inicia a inicialização Activity (o mesmo que o Xoom) - no entanto, depois de reiniciar o dispositivo após a instalação, o ícone do iniciador será retomar o aplicativo. Presumo que esses dispositivos adicionem "aplicativos instalados" a uma tabela de pesquisa na inicialização do dispositivo, permitindo que os ícones do iniciador reiniciem corretamente as tarefas em execução?

Eu já li muitas respostas que soam semelhantes ao meu problema, mas simplesmente adicionando android:alwaysRetainTaskState="true"ou usando launchMode="singleTop"oActivity não é a resposta.

Editar:

Após o lançamento mais recente deste aplicativo, descobrimos que esse comportamento começou a ocorrer em todos os dispositivos após a primeira reinicialização. O que parece loucura para mim, mas, olhando o processo de reinicialização, não consigo encontrar o que está errado.

Graeme
fonte
1
Pode parecer uma pergunta trivial, mas você definiu "Não mantenha atividades" como verdade nas suas opções de desenvolvimento para o Xoom?
Andrew Schuster
Não (desejo! :)) - registrei o ciclo de vida de cada atividade e as atividades em segundo plano como ainda disponíveis (elas foram interrompidas - não destruídas). O sistema operacional parece chamá finish()-los nos casos em que começa o primeiro Activitynovamente, em vez de continuar com eles.
Graeme
1
Se você pressionou o botão home e, em seguida, clique no ícone do iniciador, o comportamento de retomada é o padrão para o Android, como você provavelmente já sabe. No entanto, se você pressionar o botão Voltar para retornar à tela inicial, a maioria dos telefones terminará () o aplicativo. É possível que o método que você está usando para sair do aplicativo seja diferente nos diferentes dispositivos? Você poderia desconectar o onKeyUpEvent para verificar se alguns não estão lidando estranhamente com os hard / sof tkeys?
Nick Cardoso
2
Não - tenho certeza do problema, como mencionado acima. Usar home para colocar o aplicativo em segundo plano (não voltar, o que você está certo terminaria () a Atividade). É possível no Xoom retomar o aplicativo na Lista de Tarefas (e não no Iniciador), para que o backstack definitivamente não tenha sido eliminado .
Graeme
1
A resposta com a recompensa é a maneira de corrigir o problema descrito na pergunta. Marquei minha própria resposta como "correta" porque, embora às vezes o problema seja causado por um bug de aplicativo no iniciador (conforme observado em sua resposta), meu problema específico foi causado pela alternância de tarefas. A solução para ambos os problemas é corrigida por sua solução.
Graeme

Respostas:

238

O comportamento que você está enfrentando é causado por um problema que existe em alguns lançadores do Android desde a API 1. Você pode encontrar detalhes sobre o bug e possíveis soluções aqui: https://code.google.com/p/android/issues/ detail? id = 2373 .

É um problema relativamente comum em dispositivos Samsung e em outros fabricantes que usam um iniciador / capa personalizado. Não vi o problema ocorrer em um lançador de ações do Android.

Basicamente, o aplicativo não está realmente reiniciando completamente, mas a Atividade de inicialização está sendo iniciada e adicionada à parte superior da pilha de Atividades quando o aplicativo está sendo reiniciado pelo iniciador. Você pode confirmar que é esse o caso clicando no botão Voltar ao reiniciar o aplicativo e exibir a Atividade de inicialização. Você deve ser levado à atividade que você esperava que fosse exibida ao reiniciar o aplicativo.

A solução alternativa que escolhi implementar para resolver esse problema é verificar a categoria Intent.CATEGORY_LAUNCHER e a ação Intent.ACTION_MAIN na intenção que inicia a atividade inicial. Se esses dois sinalizadores estiverem presentes e a Atividade não estiver na raiz da tarefa (ou seja, o aplicativo já estava em execução), chamo de concluir () na Atividade inicial. Essa solução exata pode não funcionar para você, mas algo semelhante deve.

Aqui está o que eu faço em onCreate () da Atividade inicial / de lançamento:

    if (!isTaskRoot()
            && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
            && getIntent().getAction() != null
            && getIntent().getAction().equals(Intent.ACTION_MAIN)) {

        finish();
        return;
    }
starkej2
fonte
4
Até agora, isso funciona para mim sem efeitos colaterais adversos até agora. Com base nas suposições lógicas, não vejo razão para que isso não seja válido.
Javahead76
3
Eu acho que essa é a maneira correta de lidar com esse bug. Funciona para mim.
Sokolov
3
fez o trabalho para mim, verificado em cerca de 8 dispositivos diferentes. muito obrigado!
shaya ajzner
3
Wooooow fixo meu problema ive sido procurando uma solução para os últimos 2 horas
Jean Raymond Daher
2
Obrigado @ starkej2. Funcionou como um encanto.
Rajeev Sahu
54

Essa pergunta ainda é relevante em 2016. Hoje, um testador de controle de qualidade relatou que um aplicativo meu foi reiniciado em vez de retomar a partir do iniciador de ações no Android M.

Na realidade, o sistema estava adicionando a atividade lançada à pilha de tarefas atual , mas parecia ao usuário como se uma reinicialização tivesse ocorrido e eles tivessem perdido o trabalho. A sequência foi:

  1. Baixar da Play Store (ou sideload apk)
  2. Iniciar aplicativo a partir do diálogo da Play Store: a atividade A aparece [pilha de tarefas: A]
  3. Navegue para a atividade B [pilha de tarefas: A -> B]
  4. Pressione o botão "Início"
  5. Iniciar o aplicativo a partir da gaveta do aplicativo: a atividade A aparece! [pilha de tarefas: A -> B -> A] (o usuário pode pressionar o botão 'Voltar' para acessar a atividade 'B' a partir daqui)

Nota: esse problema não se manifesta nos APKs de depuração implantados via ADB, apenas nos APKs baixados da Play Store ou carregados lateralmente. Nos últimos casos, a intenção de lançamento da etapa 5 continha o sinalizador Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT, mas não nos casos de depuração. O problema desaparece assim que o aplicativo é iniciado a frio a partir do iniciador. Minha suspeita é que a tarefa tenha um objetivo malformado (mais precisamente, não padrão) que impede o comportamento correto de inicialização até que a tarefa seja completamente limpa.

Tentei vários modos de início de atividade , mas essas configurações se desviam muito do comportamento padrão que o usuário esperaria: continuando a tarefa na atividade B. Consulte a seguinte definição de comportamento esperado no guia Tarefas e pilha traseira , na parte inferior da página em 'Iniciando uma tarefa':

Um filtro de intenção desse tipo faz com que um ícone e um rótulo para a atividade sejam exibidos no iniciador de aplicativos, oferecendo aos usuários uma maneira de iniciar a atividade e retornar à tarefa que ela cria a qualquer momento após o lançamento.

Achei essa resposta relevante e inseri o seguinte no método 'onCreate' da minha atividade raiz (A), para que ela seja retomada adequadamente quando o usuário abrir o aplicativo.

                    /**
     * Ensure the application resumes whatever task the user was performing the last time
     * they opened the app from the launcher. It would be preferable to configure this
     * behavior in  AndroidMananifest.xml activity settings, but those settings cause drastic
     * undesirable changes to the way the app opens: singleTask closes ALL other activities
     * in the task every time and alwaysRetainTaskState doesn't cover this case, incredibly.
     *
     * The problem happens when the user first installs and opens the app from
     * the play store or sideloaded apk (not via ADB). On this first run, if the user opens
     * activity B from activity A, presses 'home' and then navigates back to the app via the
     * launcher, they'd expect to see activity B. Instead they're shown activity A.
     *
     * The best solution is to close this activity if it isn't the task root.
     *
     */

    if (!isTaskRoot()) {
        finish();
        return;
    }

UPDATE: moveu esta solução para longe da análise de sinalizadores de intenção para consultar se a atividade está na raiz da tarefa diretamente. Os sinalizadores de intenção são difíceis de prever e testar com todas as diferentes maneiras de abrir uma atividade PRINCIPAL (Iniciar em casa, iniciar a partir do botão 'para cima', iniciar na Play Store, etc.)

Rich Ehmer
fonte
4
"O problema desaparece assim que o aplicativo é iniciado a frio a partir do iniciador". Essa é a parte mais estranha e eu também a observei - matando o aplicativo após o primeiro lançamento, ele começa a se comportar normalmente novamente. Um bug tão estranho. Obrigado por analisá-lo completamente e pela solução.
Oded
11
Nota: você pode reproduzir o problema novamente após limpá-lo inicialmente ("iniciando a frio" como o descreveu) usando "Abrir" na página do Google Play do aplicativo, mesmo se você instalou o APK no Android Studio . Achei isso muito útil para verificar se a correção funcionou.
Oded
Boa explicação!
Karanatwal.github.io
Obrigado por esta explicação :)
AndroidEnthusiast
2
Isso acontece com muitos aplicativos. O Google Fotos é o principal que eu testei.
Raghubansh Mani
19

Aha! (tldr; Veja as declarações em negrito na parte inferior)

Eu encontrei o problema ... eu acho.

Então, eu vou começar com uma suposição. Quando você pressiona o iniciador, ele inicia o padrão Activityou, se um Taskiniciado por um lançamento anterior estiver aberto, ele o trará para a frente. Dito de outra maneira: se, em algum momento da sua navegação, você criar um novo Taske finishum antigo, o iniciador não retomará mais o seu aplicativo.

Se essa suposição for verdadeira, tenho certeza que isso deve ser um bug, já que cada um Taskestá no mesmo processo e é um candidato a currículo tão válido quanto o primeiro criado?

Meu problema foi corrigido removendo esses sinalizadores de um par de Intents:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );

Embora seja bastante óbvio que FLAG_ACTIVITY_NEW_TASKcrie um novo Task, não gostei que a suposição acima estivesse em vigor. Eu considerei isso um culpado e o removi para teste e ainda estava tendo um problema, então o demiti. No entanto, eu ainda tinha as condições abaixo:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

Minha tela inicial estava iniciando o "principal" Activityno meu aplicativo usando a bandeira acima. Afinal, se eu tivesse "reiniciado" meu aplicativo e Activityele ainda estivesse em execução, preferiria preservar suas informações de estado.

Você observará na documentação que não menciona o início de um novo Task:

Se definido, e a atividade iniciada já estiver em execução na tarefa atual, em vez de iniciar uma nova instância dessa atividade, todas as outras atividades serão fechadas e esse Intent será entregue ao (agora em topo) atividade antiga como uma nova Intenção.

Por exemplo, considere uma tarefa que consiste nas atividades: A, B, C, D. Se D chamar startActivity () com um Intent resolvido para o componente da atividade B, C e D serão concluídos e B receberá o Intent especificado , resultando na pilha agora sendo: A, B.

A instância atualmente em execução da atividade B no exemplo acima receberá a nova intenção que você está iniciando aqui no método onNewIntent () ou será finalizada e reiniciada com a nova intenção. Se ele declarou seu modo de inicialização como "múltiplo" (o padrão) e você não definiu FLAG_ACTIVITY_SINGLE_TOP na mesma intenção, ele será finalizado e recriado; para todos os outros modos de inicialização ou se FLAG_ACTIVITY_SINGLE_TOP estiver definido, esse Intent será entregue à onNewIntent () da instância atual.

Esse modo de inicialização também pode ser usado com bom efeito em conjunto com FLAG_ACTIVITY_NEW_TASK: se usado para iniciar a atividade raiz de uma tarefa, trará qualquer instância dessa tarefa em execução no momento para o primeiro plano e, em seguida, limpe-a ao seu estado raiz. Isso é especialmente útil, por exemplo, ao iniciar uma atividade do gerenciador de notificações.

Então, eu tive a situação conforme descrito abaixo:

  • Alançado Bcom FLAG_ACTIVITY_CLEAR_TOP, Atermina.
  • Bdeseja reiniciar um serviço para enviar o usuário ao Aqual a lógica de reinicialização do serviço e a interface do usuário (sem sinalizadores).
  • Alança Bcom FLAG_ACTIVITY_CLEAR_TOP, Atermina.

Nesse estágio, o segundo FLAG_ACTIVITY_CLEAR_TOPsinalizador está sendo reiniciado, o Bqual está na pilha de tarefas. Suponho que isso deva destruir Taske iniciar um novo, causando o meu problema, que é uma situação muito difícil de detectar, se você me perguntar!

Portanto, se toda a minha suposição estiver correta:

  • O Launcherúnico continua a tarefa criada inicialmente
  • FLAG_ACTIVITY_CLEAR_TOPse reiniciar o único restante Activity, também recriará um novoTask
Graeme
fonte
FLAG_ACTIVITY_CLEAR_TOP não cria uma nova tarefa nem reinicia a atividade que você está tentando iniciar, se for a única atividade restante. "Se definido, e a atividade iniciada já estiver em execução na tarefa atual, em vez de iniciar uma nova instância dessa atividade, todas as outras atividades serão fechadas e esse Intent será entregue ao (agora no topo) atividades antigas como uma nova Intenção ".
starkej2
2
Sei que não é para isso - mas, por tentativa e erro, isso acontece na minha instância. A remoção desses sinalizadores corrige o problema.
Graeme
Isso não cria um resumo perfeito em todos os dispositivos sob todas as condições.
precisa saber é o seguinte
A remoção dos sinalizadores corrigiu o problema para mim. Obrigado
Sealer_05
Eu tenho um cenário de tela inicial / tela principal, mas não estou usando nenhum sinalizador para fazer a transição do inicial para o principal, ainda assim o problema é reproduzível para mim - essa solução não está funcionando para mim.
ror
12

Eu tive o mesmo problema em dispositivos Samsung. Depois de pesquisar muito, nenhuma dessas respostas funcionou para mim. Descobri que no arquivo AndroidManifest.xml , launchModeestá definido como singleInstance( android:launchMode="singleInstance"). A remoção do launchModeatributo corrigiu meu problema.

Todos
fonte
Na verdade, isso fez o truque para mim também. Encontrei esta resposta de outra pergunta do SO útil: stackoverflow.com/a/21622266/293280 . E este write-se dos diferentes tipos de launchModevalores: inthecheesefactory.com/blog/...
Joshua Pinter
Essa correção funcionou para mim! Adicionado isso não no manifesto, mas no atributo de atividade na classe de atividade principal.
Calin Vlasin
@CalinVlasin, você poderia me mostrar exatamente como usou o launchMode? onde você colocou? Atualmente, tenho-o assim, mas está causando o problema: <atividade android: nome = ". UI.landing.MyActivity" android: configChanges = "localidade | layoutDirection" android: launchMode = "singleTop" android: windowSoftInputMode = "stateAlwaysHidden | AdjustResize ">
j2emanue 4/17/17
Este também foi o meu problema. Eu acho que isso deve ser usado em conjunto com a resposta aceita (isTaskRoot ...!
behelit
1

No meu Cat s60, habilitei "Não manter atividades" nas opções do desenvolvedor, desabilitar isso novamente me permitiu alternar entre aplicativos sem perder o estado dos aplicativos ...

ptp
fonte
Não faço ideia de como, mas isso estava ativado no meu dispositivo.
realPro 16/03
0

Esta solução funcionou para mim:

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Intent startMain = new Intent(Intent.ACTION_MAIN);
            startMain.addCategory(Intent.CATEGORY_HOME);
            startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(startMain);
            return false;
        }
        else
            return super.onKeyUp(keyCode, event);
    }

crédito: preciso minimizar o aplicativo Android ao clicar no botão voltar

pode não funcionar em todos os dispositivos, mas cria com êxito o comportamento do botão home quando o botão Voltar é pressionado, interrompendo a atividade em vez de terminá-la.

Russell Chisholm
fonte
Truque interessante, mas não resolve o problema como indicado. Muito útil para outros problemas / perguntas.
Graeme
O botão Voltar tem uma finalidade específica e os usuários esperam que o botão Voltar faça o que é suposto fazer. Substituí-lo de qualquer maneira está errado e, na minha opinião, altamente não profissional.
Bugs Happen
-1

Eu tive o mesmo problema, a causa foi:

(Código Kotlin, em MainActivity)

override fun onBackPressed() {
    finish()
}

Portanto, ao navegar para meu MainActivity a partir do meu LoginActivity, eu uso este:

    val intent = Intent(this, MainActivity::class.java)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(intent)

Ao usar esses sinalizadores, não preciso ter um onBackPressed () na minha MainActivity, ele sairá do aplicativo naturalmente ao clicar novamente. E ao pressionar o botão Início e voltar ao aplicativo, ele não é reiniciado.

Christer
fonte
-2

Solução para as pessoas que não têm idéia sobre a programação e o problema em seu telefone Android. Isso acontece principalmente devido à atualização da versão do Android (apenas minha suposição). Após a atualização, todos os seus aplicativos são otimizados para usar menos bateria. Mas, por sua vez, isso diminui a velocidade do seu dispositivo.

Como resolver

Vá para configurações >> Aplicativos >> configurações de aplicativos (procure as configurações em qualquer lugar da tela - é diferente em dispositivos diferentes) >> otimização da bateria (ou opção similar [insira a descrição da imagem aqui] [1] em) >> mover tudo aplicativos para o estado 'não otimizado' (é necessário fazer 1 por 1 manualmente - pode ser permitido / desabilitado em alguns telefones). O aplicativo do iniciador precisa ser "não otimizado" (iniciador de interface do usuário do Zen no meu caso - acho que esse é o culpado - você pode tentar otimizar / não otimizar e reiniciar o aplicativo diferente, se tiver tempo). Agora reinicie o seu telefone. (não é necessário redefinir os dados / modo de segurança ou qualquer problema)

Tente multitarefa agora. :) Pressionar o ícone do iniciador agora deve resultar na retomada da tarefa atual. :) Seu dispositivo se tornará Não se preocupe com a bateria, ela será descarregada de qualquer maneira.

Arun Antony
fonte
-8

Inestimável para seus usuários. O currículo perfeito, mesmo depois de semanas na lista de aplicativos usados ​​recentemente.

Parece um resumo para o usuário, mas na verdade é um começo completo.

Antecedentes: é fácil recuperar a memória usada por aplicativos que estão na atividade principal que não iniciaram uma tarefa. O sistema operacional pode simplesmente reiniciar o aplicativo com o pacote original passado para onCreate. No entanto, você pode adicionar ao pacote original no método para que o pacote original agora contenha todos os materiais e informações necessárias para construir o aplicativo, para que pareça que ele foi retomado.onSaveInstanceState assim, quando o aplicativo for reiniciado pelo sistema operacional, você poderá restaurar o estado da instância e ninguém saberá se o aplicativo foi reiniciado ou reiniciado. Tomemos, por exemplo, o programa de mapas clássico. O usuário se move para uma posição no mapa e pressiona a tecla Início. Duas semanas depois, esse aplicativo de mapeamento ainda está na lista de aplicativos recentes, juntamente com o facebook, a pandora e a paixão por doces. O sistema operacional não apenas salva o nome do aplicativo para os aplicativos usados ​​recentemente, mas também salva o pacote original usado para iniciar o aplicativo. No entanto, o programador codificou oonSaveInstanceState

Exemplo: salve a posição atual da câmera em onSaveInstanceState, apenas no caso de o aplicativo ser descarregado e ter que ser reiniciado semanas mais tarde na lista de aplicativos recentes.

@Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        // save the current camera position;
        if (mMap != null) {
            savedInstanceState.putParcelable(CAMERA_POSITION,
                    mMap.getCameraPosition());
        }
    }



@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // get the exact camera position if the app was unloaded.
        if (savedInstanceState != null) {
            // get the current camera position;
            currentCameraPosition = savedInstanceState
                    .getParcelable(CAMERA_POSITION);
        }

Nota: você também pode usar o onRestoreInstanceStatemétodo, mas acho mais fácil restaurar a instância no onCreate.

É mais do que provável o que está acontecendo no seu aplicativo. Em alguns dispositivos, seu aplicativo é descarregado para liberar memória. Sim, existem alguns sinalizadores que ajudam, mas os sinalizadores não captam todas as nuances do seu aplicativo e os sinalizadores não o mantêm vivo por semanas como o onSaveInstanceStateserão. Você precisa codificar o currículo perfeito duas semanas depois. Não será uma tarefa fácil para o aplicativo complexo, mas estamos atrás de você e estamos aqui para ajudar.

Boa sorte

danny117
fonte
Isso não se deve a problemas comuns de memória ou a condições padrão de "estado de pausa". Eu tentei meu aplicativo em vários dispositivos (alguns com memória grande, outros com memória insuficiente).
Graeme
É por isso que você salva dados persistentes no onPause.Eu tentei que meu aplicativo fosse retomado em até duas semanas no telefone que eu uso todos os dias e, depois de duas semanas, o método onCreate é chamado e o sistema operacional passa no pacote que eu salvei onSaveSessionState e eu uso os dados no pacote configurável para fazer com que minha atividade apareça exatamente como eu a deixei. Portanto, faz apenas três dias desde a minha resposta, então não há como você ter concluído um teste de duas semanas. Resumo: o aplicativo pode ser desligado a qualquer momento em segundo plano.
precisa saber é o seguinte
@ danny117 Eu não acho que você tem um preciso entender a questão que Graeme está experimentando
starkej2
Eu entendo a questão de Graeme. Em alguns dispositivos, retomar o aplicativo chamaCriar. Soa exatamente como o meu HTC EVO (Gingerbread), que mataria o aplicativo apenas para girar a tela. Olhe para os documentos que diz assim que o aplicativo pode ser restaurado em onCreate developer.android.com/reference/android/app/...
danny117
@ danny117 Isso é correto, mas a questão que ele está tendo não está relacionado a uma única atividade que está sendo recriado quando o aplicativo é retomado (que se espera), mas a atividade errada está sendo iniciado
starkej2