Anche se la tua applicazione è veloce e reattiva, alcune decisioni di progettazione possono comunque causare problemi agli utenti, a causa di interazioni non pianificate con altre applicazioni o finestre di dialogo, perdita involontaria di dati, blocchi involontari e così via. Per evitare questi problemi, è utile comprendere il contesto in cui vengono eseguite le applicazioni e le interazioni del sistema che possono influire sull'applicazione. In breve, devi cercare di sviluppare un'applicazione che interagisca perfettamente con il sistema e con altre applicazioni.
Un problema comune di continuità si verifica quando il processo in background di un'applicazione, ad esempio un servizio o un ricevitore di trasmissione, visualizza una finestra di dialogo in risposta a un determinato evento. Questo potrebbe sembrare un comportamento innocuo, soprattutto quando crei e test la tua applicazione in modo isolato, tramite l'emulatore. Tuttavia, quando l'applicazione viene eseguita su un dispositivo reale, l'applicazione potrebbe non avere lo stato attivo dell'utente nel momento in cui il processo in background mostra la finestra di dialogo. Di conseguenza, l'applicazione potrebbe visualizzare la finestra di dialogo dietro l'applicazione attiva, oppure potrebbe spostare lo stato attivo sull'applicazione corrente e mostrare la finestra di dialogo davanti a ciò che l'utente stava facendo (ad esempio, una telefonata). Questo comportamento non funzionerebbe per la tua applicazione o per l'utente.
Per evitare questi problemi, l'applicazione deve utilizzare le strutture di sistema appropriate per inviare notifiche all'utente, ovvero le classi Notification
. Utilizzando le notifiche, l'applicazione può segnalare all'utente che si è verificato un evento visualizzando un'icona nella barra di stato anziché mettere lo stato attivo sull'utente e interromperlo.
Un altro esempio di problema di continuità è quando un'attività perde inavvertitamente dati di stato o utente perché non implementa correttamente onPause() e altri metodi del ciclo di vita. In alternativa, se l'applicazione espone dati destinati a essere utilizzati da altre applicazioni, dovresti esporli tramite una classe ContentProvider, piuttosto che, ad esempio, tramite un file o un database non elaborati leggibili in tutto il mondo.
Questi esempi hanno in comune la collaborazione tra il sistema e altre applicazioni. Il sistema Android è progettato per trattare le applicazioni come una sorta di federazione di componenti a basso accoppiamento, anziché come blocchi di codice black-box. In questo modo, in qualità di sviluppatore puoi visualizzare l'intero sistema come una federazione ancora più ampia di questi componenti. Questo ti offre un vantaggio in quanto ti consente di integrare in modo pulito e senza problemi con altre applicazioni, quindi devi progettare il tuo codice per restituire il vantaggio.
Questo documento illustra i problemi di fluidità più comuni e come evitarli.
Non rilasciare dati
Tieni sempre presente che Android è una piattaforma mobile. Può sembrare ovvio dirlo, ma è importante ricordare che un'altra attività (come l'app "Telefonata in arrivo") può apparire in qualsiasi momento sopra la tua Attività. Questa operazione attiverà i metodi onSaveInstanceState() e onPause() e probabilmente la tua applicazione verrà chiusa.
Se l'utente stava modificando dati della tua applicazione quando è apparsa l'altra attività, è probabile che l'applicazione perda i dati quando l'applicazione viene chiusa. A meno che, ovviamente, tu non salvi prima il lavoro in corso. In base a questo approccio, le applicazioni Android che accettano o modificano l'input devono eseguire l'override del metodo onSaveInstanceState() e salvare il proprio stato in un modo appropriato. Quando l'utente accede di nuovo all'applicazione, dovrebbe essere in grado di recuperare i dati.
Un classico esempio di utilizzo ottimale di questo comportamento è un'applicazione di posta. Se l'utente stava scrivendo un'email quando è stata avviata un'altra attività, l'applicazione deve salvare l'email in elaborazione come bozza.
Non esporre dati non elaborati
Se non cammini per strada indossando la biancheria intima, neanche i tuoi dati dovrebbero farlo. Sebbene sia possibile esporre alcuni tipi di applicazioni per la lettura, in genere questa non è l'idea migliore. L'esposizione dei dati non elaborati richiede altre applicazioni per comprendere il formato dei dati; se modifichi il formato, si interromperanno tutte le altre applicazioni che non sono aggiornate in modo simile.
Il metodo Android consente di creare una classe ContentProvider per esporre i dati ad altre applicazioni tramite un'API pulita, ben strutturata e gestibile. L'utilizzo di una classe ContentProvider è molto simile all'inserimento di un'interfaccia del linguaggio Java per suddividere e componenti due parti di codice strettamente collegate. Ciò significa che potrai modificare il formato interno dei dati senza modificare l'interfaccia esposta da ContentProvider, senza influire su altre applicazioni.
Non interrompere l'utente
Se l'utente esegue un'applicazione (ad esempio l'applicazione Telefono durante una chiamata), è piuttosto sicuro che l'abbia fatto di proposito. Ecco perché dovresti evitare di generare attività, se non in risposta diretta all'input utente dall'attività corrente.
Vale a dire che non chiamare startActivity() da BroadcastRicevir o servizi in esecuzione in background. L'operazione interromperà qualsiasi applicazione attualmente in esecuzione e darà fastidio all'utente. Forse ancora peggio, la tua Attività potrebbe diventare un "tasso di tasti" e ricevere parte degli input che l'utente stava fornendo all'attività precedente. A seconda di ciò che fa la tua applicazione, potrebbe trattarsi di una cattiva notizia.
Invece di creare le UI delle attività direttamente in background, devi usare NotificationManager per impostare le notifiche. Questi vengono visualizzati nella barra di stato e l'utente può selezionarli quando preferisce per vedere che cosa deve mostrare la tua applicazione.
Tieni presente che tutto questo non si applica ai casi in cui la tua attività è già in primo piano: in questo caso, l'utente si aspetta di vedere la tua attività successiva in risposta all'input.
Hai molto da fare? Fallo in un thread
Se la tua applicazione deve eseguire calcoli costosi o a lunga esecuzione, probabilmente dovresti spostarla in un thread. Ciò impedirà all'utente di visualizzare la temuta finestra di dialogo "L'applicazione non risponde" e il risultato finale è la morte dell'applicazione.
Per impostazione predefinita, tutto il codice in un'attività e tutte le relative viste vengono eseguite nello stesso thread. Si tratta dello stesso thread che gestisce anche gli eventi UI. Ad esempio, quando l'utente preme un tasto, viene aggiunto un evento Key-down alla coda del thread principale dell'attività. Il sistema del gestore di eventi deve rimuovere la coda e gestire l'evento rapidamente; in caso contrario, dopo alcuni secondi il sistema termina il blocco dell'applicazione e si offre di terminarlo per l'utente.
Se hai codice a lunga esecuzione, l'esecuzione incorporata nell'attività lo eseguirà sul thread del gestore di eventi, bloccando di fatto il gestore di eventi. Questa operazione ritarda l'elaborazione dell'input e genera finestre di dialogo ANR. Per evitare che questo accada, sposta i calcoli in un thread. Il documento Progetta per la reattività spiega come fare.
Schermata Non sovraccaricare una singola attività
Probabilmente ogni applicazione che vale la pena utilizzare avrà diversi schermi. Quando progetti le schermate dell'interfaccia utente, assicurati di utilizzare più istanze di oggetti Activity.
In base alle tue conoscenze di sviluppo, puoi interpretare un'attività come simile a un'applet Java, in quanto è il punto di ingresso per la tua applicazione. Tuttavia, ciò non è del tutto preciso: se una sottoclasse Applet è il singolo punto di ingresso per un'applet Java, un'attività dovrebbe essere considerata come uno dei vari punti di ingresso potenzialmente all'applicazione. L'unica differenza tra l'attività "principale" e le eventuali altre è che quella "principale" è l'unica che ha espresso interesse per l'azione "android.intent.action.MAIN" nel file AndroidManifest..xml.
Quando progetti la tua applicazione, pensa all'applicazione come a una federazione di oggetti Attività. Ciò renderà il tuo codice molto più gestibile nel lungo periodo e, come simpatico effetto collaterale, è adatto anche alla cronologia delle applicazioni e al modello "backstack" di Android.
Estendi i temi del sistema
L'aspetto e il design dell'interfaccia utente sono fondamentali. Gli utenti sono sconvolti dalle applicazioni che sono in contrasto con l'interfaccia utente che si aspettano. Quando progetti le interfacce utente, cerca di evitare di implementarle il più possibile. Utilizza invece un tema. Puoi eseguire l'override o estendere queste parti del tema che ti servono, ma almeno partirai dalla stessa base di interfaccia utente di tutte le altre applicazioni. Per tutti i dettagli, consulta Stili e temi.
Progetta l'interfaccia utente in modo che funzioni con più risoluzioni dello schermo
I diversi dispositivi Android supporteranno risoluzioni dello schermo diverse. Alcuni potranno anche cambiare la risoluzione all'istante, ad esempio passando alla modalità Orizzontale. È importante assicurarsi che layout e disegni siano sufficientemente flessibili da poter essere visualizzati correttamente su vari schermi dei dispositivi.
Fortunatamente, l'operazione è molto semplice. In breve, ciò che devi fare è fornire versioni diverse dell'artwork (se le utilizzi) per i principali risoluzioni, quindi progettare il layout in modo da adattarlo a varie dimensioni. Ad esempio, evita di utilizzare posizioni hardcoded e utilizza invece layout relativi. Se fai così tanto, il sistema si occuperà del resto e l'applicazione verrà visualizzata correttamente su qualsiasi dispositivo.
Dare per scontato che la rete sia lenta
I dispositivi Android offrono una varietà di opzioni per la connettività di rete. Tutti avranno un certo provisioning dell'accesso ai dati, anche se alcuni saranno più veloci di altri. Il minimo comune denominatore, tuttavia, è GPRS, il servizio dati non 3G per le reti GSM. Anche i dispositivi 3G passeranno molto tempo sulle reti non 3G, quindi le reti lente rimarranno una realtà per molto tempo a venire.
Per questo motivo dovresti sempre programmare le tue applicazioni in modo da ridurre al minimo gli accessi alla rete e la larghezza di banda. Non si può dare per scontato che la rete sia veloce, perciò conviene sempre che sia lenta. Se i tuoi utenti utilizzano reti più veloci, è fantastico: la loro esperienza non potrà che migliorare. Tuttavia, è consigliabile evitare il caso inverso: le applicazioni che sono utilizzabili nella maggior parte del tempo, ma che rallentano in modo frustrante le altre in base alla posizione dell'utente in un dato momento, è probabile che siano impopolari.
Un potenziale problema è che è molto facile cadere in questa trappola usando l'emulatore, dato che usa la connessione di rete del computer. Quasi sicuramente sarà molto più veloce di una rete mobile, quindi ti consigliamo di modificare le impostazioni nell'emulatore che simulano velocità di rete più lente. Puoi eseguire questa operazione in Android Studio tramite AVD Manager o tramite un'opzione della riga di comando quando avvii l'emulatore.
Non utilizzare touchscreen o tastiera
Android supporterà una varietà di fattori di forma degli smartphone. È un modo strano per dire che alcuni dispositivi Android avranno tastiere "QWERTY" complete, mentre altri avranno configurazioni a 40, 12 tasti o anche altre configurazioni. Analogamente, alcuni dispositivi dispongono di touchscreen, ma molti no.
Tienilo a mente quando crei le tue applicazioni. Non dare ipotesi su layout di tastiera specifici, a meno che tu non voglia davvero limitare la tua applicazione in modo che possa essere utilizzata solo su quei dispositivi.
Conserva la batteria del dispositivo
Un dispositivo mobile non è molto mobile se è costantemente collegato alla parete. I dispositivi mobili sono alimentati a batteria e più a lungo possiamo far durare la batteria con una ricarica, più soddisfatti saranno tutti, soprattutto l'utente. Due dei maggiori consumatori di energia a batteria sono il processore e la radio; ecco perché è importante scrivere le tue applicazioni in modo che svolgano il meno lavoro possibile e utilizzino la rete il meno frequentemente possibile.
Ridurre al minimo la quantità di tempo del processore utilizzata dall'applicazione dipende in realtà dalla scrittura di un codice efficiente. Per ridurre al minimo il consumo di corrente dovuto all'uso della radio, assicurati di gestire agevolmente le condizioni di errore e di recuperare solo ciò che ti serve. Ad esempio, non riprovare costantemente un'operazione di rete se una non è andata a buon fine. Se il problema persiste una volta, è probabile che l'utente non abbia ricezione, quindi probabilmente l'errore si ripresenta se provi immediatamente. Non devi fare altro che sprecare batteria.
Gli utenti sono piuttosto intelligenti: se il tuo programma richiede molta energia, puoi contare su di loro se ne accorgeranno. L'unica cosa di cui puoi essere certo a quel punto è che il programma non rimarrà installato molto a lungo.