L'app si riavvia anziché riprendere


195

Spero che qualcuno possa aiutarmi a capire, se non una soluzione, almeno una spiegazione per un comportamento.

Il problema:

Su alcuni dispositivi, premendo l'icona del programma di avvio si ripristina l'attività corrente, in altri si avvia l'attivazione iniziale del lancio (riavvio effettivo dell'app). Perché succede?

Il dettaglio:

Quando si preme l'icona di avvio, l'app si avvia normalmente, ovvero, presumo, viene avviato un Intento con il nome del primo utente Activitycon l'azione android.intent.action.MAINe la categoria android.intent.category.LAUNCHER. Questo non può essere sempre il caso:

Sulla maggior parte dei dispositivi, se si preme l'icona di avvio dopo che l'app è già in esecuzione, viene ripristinata l'attività attualmente in esecuzione in quel processo ( NON l'iniziale Activity). Riprende come se l'avessi selezionato da "Attività recenti" nel menu OS. Questo è il comportamento che desidero su tutti i dispositivi.

Tuttavia, su altri dispositivi selezionati si verifica un comportamento diverso:

  • Sul Motorola Xoom, quando si preme l'icona di avvio, l'app avvia sempre l' avvio iniziale Activityindipendentemente da ciò che è attualmente in esecuzione. Presumo che le icone di avvio avviino sempre l'intento "LANCIO".

  • Sul Samsung Tab 2, quando premi l'icona di avvio, se hai appena installato l'app, avvierà sempre l'iniziale Activity(uguale a Xoom) - tuttavia, dopo aver riavviato il dispositivo dopo l'installazione, l'icona di avvio sarà invece riprendi l'app. Presumo che questi dispositivi aggiungano "app installate" in una tabella di ricerca all'avvio del dispositivo che consenta alle icone di avvio di riprendere correttamente le attività in esecuzione?

Ho letto molte risposte che sembrano simili al mio problema, ma semplicemente l'aggiunta android:alwaysRetainTaskState="true"o l'utilizzo launchMode="singleTop"di Activitynon sono la risposta.

Modificare:

Dopo il lancio più recente di questa app, scopriamo che questo comportamento ha iniziato a manifestarsi su tutti i dispositivi dopo il primo riavvio. Il che mi sembra folle ma guardando attraverso il processo di riavvio, non riesco davvero a trovare cosa non va.


1
Può sembrare una domanda banale da porre, ma hai impostato "Non mantenere le attività" su true nelle opzioni di sviluppo per Xoom?
Andrew Schuster,

No (desidero! :)) - Ho registrato il ciclo di vita di ogni attività e le attività in background come ancora disponibili (sono fermi - non distrutti). Il sistema operativo sembra richiamarli finish()nei casi in cui ricomincia il primo Activityinvece di riprenderli.
Graeme,

1
Se hai premuto il pulsante Home e quindi fai clic sull'icona di avvio, il comportamento di ripristino è l'impostazione predefinita per Android, come probabilmente saprai. Tuttavia, se si preme il pulsante Indietro per tornare alla schermata principale, la maggior parte dei telefoni finirà () l'app. È possibile che il metodo utilizzato per uscire dall'app sia diverso sui diversi dispositivi? Potresti disconnetterti da onKeyUpEvent per verificare che alcuni non stiano gestendo le chiavi hard / sof in modo strano?
Nick Cardoso,

2
No - Sono sicuro del problema come indicato sopra. Usando home per mettere l'app in background (non indietro, che hai ragione finiresti () l'attività). Su Xoom è possibile riprendere l'app dall'Elenco attività (ma non dal Launcher), quindi lo zaino non è stato definitivamente ucciso.
Graeme,

1
La risposta con la generosità è il modo per risolvere il problema descritto nella domanda. Contrassegna la mia risposta come "corretta" perché, sebbene a volte il problema sia causato da un bug dell'app nel programma di avvio (come indicato nella sua risposta), il mio problema particolare è stato causato dal cambio di attività. La soluzione a entrambi i problemi è risolta dalla sua soluzione.
Graeme,

Risposte:


238

Il comportamento riscontrato è causato da un problema che si verifica in alcuni launcher Android dall'API 1. Puoi trovare i dettagli sul bug e le possibili soluzioni qui: https://code.google.com/p/android/issues/ dettaglio? id = 2373 .

È un problema relativamente comune sui dispositivi Samsung e su altri produttori che utilizzano un launcher / skin personalizzato. Non ho visto il problema verificarsi su un launcher Android di serie.

Fondamentalmente, l'app non si riavvia completamente, ma l'attività di avvio viene avviata e aggiunta nella parte superiore dello stack di attività quando l'app viene riavviata dal programma di avvio. Puoi confermare questo caso facendo clic sul pulsante Indietro quando riprendi l'app e ti viene mostrata l'attività di avvio. Dovresti quindi essere portato all'attività che ti aspettavi di essere mostrato quando hai ripreso l'app.

La soluzione alternativa che ho scelto di implementare per risolvere questo problema è verificare la categoria Intent.CATEGORY_LAUNCHER e l'azione Intent.ACTION_MAIN nell'intento che avvia l'attività iniziale. Se questi due flag sono presenti e l'attività non è alla radice dell'attività (il che significa che l'app era già in esecuzione), allora chiamo finish () sull'attività iniziale. Quella soluzione esatta potrebbe non funzionare per te, ma qualcosa di simile dovrebbe.

Ecco cosa faccio in onCreate () dell'attività iniziale / di avvio:

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

        finish();
        return;
    }

4
Finora questo funziona per me senza effetti collaterali finora. Sulla base delle ipotesi logiche, non vedo alcun motivo per cui non sia valido.
javahead76,

3
Penso che questo sia il modo corretto di affrontare questo bug. Per me va bene.
Sokolov,

3
ha fatto il lavoro per me, verificato su circa 8 diversi dispositivi. grazie mille!
Shaya Ajjner,

3
WOOOOOW ha risolto il mio problema mentre cercavo una soluzione per le ultime 2 ore
Jean Raymond Daher,

2
Grazie @ starkej2. Ha funzionato come un fascino.
Rajeev Sahu,

55

Questa domanda è ancora pertinente nel 2016. Oggi un tester per il controllo qualità ha segnalato il mio riavvio di un'app anziché riprendere dal lanciatore di azioni in Android M.

In realtà, il sistema stava aggiungendo l'attività avviata allo stack di attività corrente , ma sembrava all'utente come se si fosse verificato un riavvio e avevano perso il lavoro. La sequenza era:

  1. Scarica dal Play Store (o apid sideload)
  2. Avvia l'app dalla finestra di dialogo del Play Store: viene visualizzata l'attività A [task stack: A]
  3. Passare all'attività B [stack attività: A -> B]
  4. Premi il pulsante 'Home'
  5. Avvia l'app dal cassetto delle app: viene visualizzata l'attività A! [task stack: A -> B -> A] (l'utente può premere il pulsante 'Indietro' per accedere all'attività 'B' da qui)

Nota: questo problema non si manifesta per gli APK di debug distribuiti tramite ADB, solo negli APK scaricati dal Play Store o caricati lateralmente. In questi ultimi casi, l'intento di lancio dal passaggio 5 conteneva il flag Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT, ma non nei casi di debug. Il problema scompare una volta che l'app è stata avviata a freddo dal programma di avvio. Il mio sospetto è che l'attività sia seminata con un intento non corretto (più precisamente, non standard) che impedisce il corretto comportamento di avvio fino a quando l'attività non viene completamente cancellata.

Ho provato varie modalità di avvio dell'attività , ma quelle impostazioni si discostano troppo dal comportamento standard che l'utente si aspetterebbe: riprendere l'attività all'attività B. Vedere la seguente definizione di comportamento previsto nella guida alle attività e allo stack posteriore , nella parte inferiore della pagina in "Avvio di un'attività":

Un filtro intento di questo tipo fa sì che un'icona e un'etichetta per l'attività vengano visualizzate nel programma di avvio dell'applicazione, offrendo agli utenti un modo per avviare l'attività e tornare all'attività che crea in qualsiasi momento dopo che è stata avviata.

Ho trovato questa risposta pertinente e ho inserito quanto segue nel metodo "onCreate" della mia attività di root (A) in modo che riprenda in modo appropriato quando l'utente apre l'applicazione.

                    /**
     * 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;
    }

AGGIORNAMENTO: spostato questa soluzione dall'analisi dei flag di intenti alla query se l'attività è direttamente alla radice dell'attività. I flag di intenti sono difficili da prevedere e testare in tutti i modi in cui è possibile aprire un'attività PRINCIPALE (Avvio da casa, avvio dal pulsante "su", avvio dal Play Store, ecc.)


4
"Il problema scompare una volta che l'app è stata avviata a freddo dal programma di avvio." Questa è la parte più strana e l'ho osservata anche io: uccidendo l'app dopo il primo avvio, ricomincia a comportarsi normalmente. Un bug così strano. Grazie per averlo analizzato in modo così completo e per la soluzione.
Oded,

11
Nota: è possibile ottenere nuovamente la riproduzione del problema dopo averlo cancellato inizialmente (tramite "avvio a freddo" come descritto) utilizzando "Apri" dalla pagina Google Play dell'app, anche se l'APK è stato installato tramite Android Studio . Ho trovato questo molto utile per verificare che la correzione funzionasse.
Oded,

Bella spiegazione!
karanatwal.github.io

Grazie per questa spiegazione :)
AndroidEnthusiast

2
Questo succede con molte app. Google Foto è uno dei principali che ho testato.
Raghubansh Mani,

19

Aha! (tldr; vedi le dichiarazioni in grassetto in fondo)

Ho trovato il problema ... Penso.

Quindi, inizierò con una supposizione. Quando si preme il programma di avvio, viene avviato il valore predefinito Activityo, se è stato Taskavviato un avvio precedente, lo porta in primo piano. In altre parole: se in qualsiasi fase della navigazione crei una nuova Taske finishuna vecchia, il programma di avvio non riprenderà più la tua app.

Se questa supposizione è vera, sono abbastanza sicuro che dovrebbe essere un bug, dato che ognuno Taskè nello stesso processo ed è valido come candidato per il curriculum quanto il primo creato?

Il mio problema, quindi, è stato risolto rimuovendo questi flag da un paio di Intents:

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

Mentre è abbastanza ovvio che FLAG_ACTIVITY_NEW_TASKcrea un nuovo Task, non ho apprezzato che la supposizione di cui sopra fosse in vigore. Ho considerato questo un colpevole e l'ho rimosso per testare e stavo ancora avendo un problema, quindi l'ho respinto. Tuttavia, avevo ancora le seguenti condizioni:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

La mia schermata iniziale avviava la "principale" Activitynella mia app usando il flag sopra. Dopotutto, se avessi "riavviato" la mia app e Activityfosse ancora in esecuzione, preferirei piuttosto preservare le informazioni sullo stato.

Noterai nella documentazione che non fa menzione dell'avvio di un nuovo Task:

Se impostato e l'attività in corso è già in esecuzione nell'attività corrente, quindi invece di avviare una nuova istanza di tale attività, tutte le altre attività al di sopra di essa verranno chiuse e questo Intento verrà consegnato al (ora in alto) vecchia attività come nuovo Intento.

Ad esempio, considera un'attività consistente nelle attività: A, B, C, D. Se D chiama startActivity () con un Intento che si risolve nel componente dell'attività B, allora C e D saranno finiti e B riceverà l'intento dato , per cui lo stack ora è: A, B.

L'istanza attualmente in esecuzione dell'attività B nell'esempio sopra o riceverà il nuovo intento che si sta iniziando qui nel suo metodo onNewIntent (), oppure sarà esso stesso finito e riavviato con il nuovo intento. Se ha dichiarato che la sua modalità di avvio è "multipla" (impostazione predefinita) e non hai impostato FLAG_ACTIVITY_SINGLE_TOP nello stesso intento, verrà terminata e ricreata; per tutte le altre modalità di avvio o se è impostato FLAG_ACTIVITY_SINGLE_TOP, questo intento verrà consegnato all'istanza corrente su NewIntent ().

Questa modalità di avvio può anche essere utilizzata con buoni risultati in combinazione con FLAG_ACTIVITY_NEW_TASK: se utilizzata per avviare l'attività di root di un'attività, porta in primo piano qualsiasi istanza attualmente in esecuzione di quell'attività, quindi la cancella al suo stato principale. Ciò è particolarmente utile, ad esempio, quando si avvia un'attività dal gestore delle notifiche.

Quindi, ho avuto la situazione come descritto di seguito:

  • Alanciato Bcon FLAG_ACTIVITY_CLEAR_TOP, Afiniture.
  • Bdesidera riavviare un servizio, quindi invia l'utente al Aquale ha la logica di riavvio del servizio e l'interfaccia utente (nessun flag).
  • Aviene avviato Bcon FLAG_ACTIVITY_CLEAR_TOP, Atermina.

A questo punto FLAG_ACTIVITY_CLEAR_TOPsi riavvia il secondo flag Bche si trova nello stack di attività. Suppongo che questo debba distruggere Taske avviarne uno nuovo, causando il mio problema, che è una situazione molto difficile da individuare se me lo chiedi!

Quindi, se tutte le mie supposizioni sono corrette:

  • L' Launcherunico riprende l'attività creata inizialmente
  • FLAG_ACTIVITY_CLEAR_TOPse riavvia l'unico rimanente Activity, ricrea anche un nuovoTask

FLAG_ACTIVITY_CLEAR_TOP non crea una nuova attività né riavvia l'attività che stai tentando di avviare se è l'unica attività rimasta. "Se impostato e l'attività in corso è già in esecuzione nell'attività corrente, quindi invece di avviare una nuova istanza di tale attività, tutte le altre attività al di sopra di essa verranno chiuse e questo Intento verrà consegnato al (ora in alto) vecchia attività come nuovo intento ".
starkej2,

2
Mi rendo conto che non è pensato per - ma attraverso tentativi ed errori questo accade nel mio caso. La rimozione di questi flag risolve il problema.
Graeme,

Questo non crea un curriculum perfetto su tutti i dispositivi in ​​tutte le condizioni.
danny117,

La rimozione delle bandiere ha risolto il problema per me. Grazie
Sealer_05

Ho uno splash screen / scenario della schermata principale ma non sto usando alcun flag per passare da splash a main, tuttavia il problema è riproducibile per me - questa soluzione non funziona per me.
ror

12

Ho avuto lo stesso problema sui dispositivi Samsung. Dopo aver cercato molto, nessuna di queste risposte ha funzionato per me. Ho scoperto che nel file AndroidManifest.xmllaunchMode è impostato su singleInstance( android:launchMode="singleInstance"). La rimozione launchModedell'attributo ha risolto il mio problema.


In effetti, questo ha funzionato anche per me. Ho trovato utile questa risposta da un'altra domanda SO: stackoverflow.com/a/21622266/293280 . E questo annota i diversi tipi di launchModevalori: inthecheesefactory.com/blog/…
Joshua Pinter,

Questa correzione ha funzionato per me! Aggiunto questo non in manifest ma in attributo di attività sulla classe di attività principale.
Calin Vlasin,

@CalinVlasin potresti mostrarmi esattamente come hai usato launchMode? dove l'hai posizionato? attualmente ce l'ho così ma sta causando il problema: <attività android: name = ". UI.landing.MyActivity" android: configChanges = "locale | layoutDirection" android: launchMode = "singleTop" android: windowSoftInputMode = "stateAlwaysHidden | AdjustResize ">
j2emanue il

Anche questo era il mio problema. Penso che questo dovrebbe essere usato insieme alla risposta accettata (! IsTaskRoot ...
behelit

1

Sul mio Cat s60, avevo abilitato "Non conservare attività" nelle Opzioni sviluppatore, disabilitandolo di nuovo mi permettevo di cambiare app senza perdere lo stato delle app ...


Non ho idea di come, ma questo era ON sul mio dispositivo.
realPro

0

Questa soluzione ha funzionato per me:

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

credito: ho bisogno di ridurre a icona l'applicazione Android al clic sul pulsante Indietro

potrebbe non funzionare su tutti i dispositivi, ma crea correttamente il comportamento del pulsante Home quando si preme il pulsante Indietro, interrompendo così l'attività anziché terminarla.


Trucco interessante ma non risolve il problema come indicato. Molto utile per altri problemi / domande però.
Graeme,

Il pulsante Indietro ha uno scopo specifico e gli utenti si aspettano che il pulsante Indietro faccia quello che dovrebbe fare. Ignorarlo in alcun modo è sbagliato e, secondo me, altamente poco professionale.
Bugs Happen

-1

Ho avuto lo stesso problema, la causa era:

(Codice Kotlin, in MainActivity)

override fun onBackPressed() {
    finish()
}

Quindi quando si accede a MainActivity da LoginActivity, utilizzo questo:

    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)

Quando utilizzo questi flag non devo avere un onBackPressed () nella mia MainActivity, uscirà naturalmente dall'applicativo facendo clic con il tasto destro. E quando si preme il pulsante Home e si torna all'app, non si riavvia.


-2

Soluzione per le persone che non hanno idea di programmare e sperimentare questo problema nel proprio telefono Android. Ciò accade principalmente a causa dell'aggiornamento della versione di Android (solo il mio presupposto). Dopo l'aggiornamento tutte le tue app vengono ottimizzate per utilizzare meno batteria. Ma questo a sua volta rallenta il tuo dispositivo.

Come risolvere

Vai alle impostazioni >> App >> impostazioni app (cerca il segno delle impostazioni in qualsiasi punto dello schermo - è diverso su diversi dispositivi) >> ottimizzazione della batteria (o opzioni simili [inserisci la descrizione dell'immagine qui] [1] su) >> sposta tutto le app allo stato "non ottimizzato" (devono essere eseguite manualmente 1 per 1, in alcuni telefoni potrebbe essere consentito / non consentito). La tua app di avvio deve essere "non ottimizzata" (nel mio caso il launcher dell'interfaccia utente Zen - questo è il colpevole, immagino - potresti provare a ottimizzare / Non ottimizzare e riavviare app diverse se hai tempo). Ora riavvia il telefono. (non è necessario ripristinare i dati / modalità sicura o problemi)

Prova ora il multitasking. :) La pressione dell'icona di avvio dovrebbe ora comportare la ripresa dell'attività corrente. :) Il tuo dispositivo diventerà non preoccuparti della batteria, si scaricherà comunque.


-8

Inestimabile per i tuoi utenti. Il curriculum perfetto anche dopo settimane di seduta nell'elenco delle app utilizzate di recente.

Sembra un curriculum per l'utente, ma in realtà è un inizio completo.

Sfondo: la memoria utilizzata dalle app che si trovano nell'attività principale che non ha avviato un'attività è facile da recuperare. Il sistema operativo può semplicemente riavviare l'app con il bundle originale passato a onCreate. Puoi comunque aggiungere al bundle originale in onSaveInstanceStatemodo che quando l'app viene riavviata dal sistema operativo puoi ripristinare lo stato dell'istanza e nessuno è più saggio sul fatto che l'app sia riavviata o ripresa. Prendi ad esempio il classico programma cartografico. L'utente si sposta in una posizione sulla mappa e quindi preme il tasto Home. Due settimane dopo questa app di mappatura è ancora nell'elenco delle app recenti insieme a Facebook, Pandora e Candy Crush. Il sistema operativo non solo salva il nome dell'app per le app utilizzate di recente, ma salva anche il bundle originale utilizzato per avviare l'app. Tuttavia, il programmatore ha codificato ilonSaveInstanceState quindi il bundle originale contiene ora tutti i materiali e le informazioni necessarie per costruire l'app in modo che sembri che sia stata ripresa.

Esempio: salva la posizione corrente della telecamera in onSaveInstanceState nel caso in cui l'app sia scarica e debba essere riavviata settimane dopo dall'elenco delle app recenti.

@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: puoi anche usare il onRestoreInstanceStatemetodo ma trovo più facile ripristinare l'istanza in onCreate.

Questo è molto probabilmente ciò che sta accadendo nella tua app. Su alcuni dispositivi l'app viene scaricata nella memoria libera. Sì, ci sono alcune bandiere che aiutano ma le bandiere non rileveranno ogni sfumatura della tua app e le bandiere non ti manterranno in vita per settimane come onSaveInstanceStatefaranno. Devi codificare il perfetto curriculum due settimane dopo. Non sarà un compito facile per l'app complessa, ma siamo dietro di te e siamo qui per aiutarti.

In bocca al lupo


Ciò non è dovuto a problemi di memoria comuni o condizioni di "stato di pausa" predefinite. Ho provato la mia app su molti dispositivi (alcuni con memoria grande, altri con memoria inferiore).
Graeme,

Questo è il motivo per cui salvi i dati persistenti in onPause Ho provato la mia app a riprendere fino a due settimane al telefono che uso tutti i giorni e lascia che ti dica dopo due settimane il metodo onCreate viene chiamato e il sistema operativo passa nel bundle in cui ho salvato onSaveSessionState e io utilizziamo i dati nel bundle per far apparire la mia attività esattamente come l'ho lasciata. Quindi sono passati solo tre giorni dalla mia risposta, quindi non è possibile che tu abbia completato un test di due settimane. Riepilogo: l'app può essere chiusa ogni volta che si trova in background.
danny117,

@ danny117 Non credo che tu abbia una comprensione accurata del problema che Graeme sta vivendo
starkej2,

Capisco il problema di Graeme. Su alcuni dispositivi riprendendo l'app chiama onCreate. Sembra esattamente come il mio HTC EVO (Gingerbread) che ucciderebbe l'app solo per ruotare lo schermo. Guarda i documenti che dice in modo che l'app possa essere ripristinata in onCreate developer.android.com/reference/android/app/…
danny117

@danny117 è corretto, ma il problema che sta riscontrando non è correlato a una singola attività che viene ricreata quando l'app viene ripresa (che è prevista), ma viene avviata un'attività sbagliata
starkej2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.