In questo tutorial spiegheremo il come si comporta Android durante l'esecuzione di un servizio, descriveremo in cosa consistono i thread di esecuzione e di cosa trattano i processi. Questo ci permetterà di capire come vengono eseguite le nostre applicazioni, dandoci maggiore controllo e stabilità sui dispositivi mobili dove verranno installate.
Filo
Quando l'utente esegue un'applicazione, Android crea un thread chiamato main (main). Questo thread è molto importante perché si occupa di gestire gli eventi che l'utente attiva ai componenti appropriati e include anche gli eventi che disegnano lo schermo. Un thread di esecuzione, la parte più piccola che può essere elaborata da uno scheduler in un sistema operativo, in questo caso Android (con kernel Linux).
Il implementazione di più thread che vengono elaborati contemporaneamente nella stessa applicazione, (chiamatela concorrenza, che si riferisce alla simultaneità di esecuzione), è noto come multithreading. Il multithreading viene applicato in modo che questi thread condividano le risorse E questo è ciò che comprende un processo, ricorda che questo può essere applicato a livello di codice all'interno del codice della stessa applicazione, l'implementazione del multithreading a livello di sistema operativo non dipende da noi.
L'inizio del ciclo di vita di un'applicazione include la generazione di un nuovo processo Linux a cui viene assegnato un thread principale o thread dell'interfaccia utente (il thread che è responsabile dell'elaborazione grafica dell'applicazione, thread dell'interfaccia utente in inglese).
NotaIl ciclo di vita include l'esecuzione dei metodi: onCreate(), onStart() e onResume(); all'inizio e alla fine: onPause(), onStop() e onDestroy().
Un processo può essere forzato a chiudersi da Android a causa della mancanza di memoria, questo tipo di caso è raro a causa del progresso tecnologico ma succede ancora.
La domanda è: Quali processi Android decide di chiudere?
Questi sono chiusi confrontando il loro livello di importanza, è riassunto come segue:
Il più importante: processi in primo pianoL'utente sta interagendo con detto processo (il metodo onResume() di detto processo è attualmente in esecuzione). Esiste un servizio che esegue i suoi metodi del ciclo di vita. O c'è un? BroadcastReceiver correndo il suo metodo onReceive().
Il secondo più importante: Processi visibiliAttività con chiamata a metodo onPause(). Servizio collegato a un'attività visibile (servizio associato).
Il terzo più importante: Processo con un servizioL'utente non sta interagendo direttamente con il processo. Il processo ha un servizio in esecuzione in background.
Il secondo meno importante: processo in backgroundNon c'è nessun tipo di interazione con l'utente. Il processo visualizzato più di recente dall'utente sarà l'ultimo ad essere distrutto.
Il meno importante: processo vuotoNon ha componenti attivi. Il processo è ancora attivo per scopi di memorizzazione nella cache, impedendo all'utente di tornare a utilizzare quel processo.
Quest'ultimo, il processo vuoto, è il primo ad essere terminato in caso di mancanza di memoria. Pertanto, un'applicazione che implementa un servizio in cui viene creato un thread per scaricare contenuti da Internet, sarà più importante di un'applicazione che crea il thread senza implementare un servizio, quindi è più probabile che venga terminata prima di completare il download. , perché sono processi di lunga durata.
Per capire il multhreading vediamo come Android gestisce il suo thread principale.
IL PROCESSO A ha un'interfaccia utente o filo PRINCIPALE, questo thread gestisce un coda messaggi o coda dei messaggi, che viene eseguita quando il thread diventa inattivo, chi lo gestisce? Il Looper.
Looper è una classe di interfaccia utente di Android Java che, insieme al Classe gestore, elabora gli eventi dell'interfaccia utente come la pressione dei pulsanti, le schermate ridisegnate e gli interruttori di orientamento. Gli eventi possono essere utilizzati anche per caricare contenuto in un servizio HTTP, ridimensionare le immagini ed eseguire richieste remote. La caratteristica chiave di queste classi è che sono in grado di implementare un modello di concorrenza.
Il Classe Android Looper contiene a MessageQueue (coda messaggi) ed è associato solo all'argomento da cui è stato creato. Si prega di notare che questa connessione non può essere interrotta e che il lLooper non può essere collegato a nessun altro thread. Inoltre, il Looper è nella memoria locale e può essere chiamato solo da un metodo statico. Un metodo di staging verifica se un Looper è già associato a un thread, quindi il metodo statico crea il Looper. Successivamente, è possibile utilizzare un ciclo per controllare i messaggi in coda.
Finora comprendiamo diversi concetti: processo, thread, thread dell'interfaccia utente, looper, ma non sappiamo ancora perché il multithreading.
Operazioni a lungo termine
È considerata di lunga durata qualsiasi metodo la cui esecuzione superi i 5 secondi, che fa scattare il tipico messaggio “l'applicazione non risponde. Vuoi chiuderlo?
Quali possono essere queste operazioni?: Accesso a Internet, Query SQL, analisi XML/HTML/JSON, elaborazione grafica complessa. Qualsiasi di queste operazioni eseguite nel thread principale lo bloccherà e poiché è quello che gestisce l'interfaccia utente grafica, viene interpretato come un blocco, che Android decide di chiudere.
Immaginiamo solo che una qualsiasi di queste operazioni duri 7 secondi e l'utente decida di scrivere qualcosa in un input di testo, quindi mentre questi 7 secondi non sono trascorsi, il thread dell'interfaccia utente non può aggiornare la vista in modo che l'utente apprezzi che sta scrivendo e quindi genera un blocco, viene attivato il messaggio "nessuna risposta" con il quale hai due opzioni, attendi o distruggi, anche se non puoi mai sapere quanto tempo aspettare, potrebbe essere alcuni secondi o addirittura minuti a seconda della coda dei messaggi che hanno il thread principale.
Come evitiamo il congelamento?
Utilizzando thread o servizi, a seconda che l'attività richieda la modifica della visualizzazione, in questo caso viene implementato un servizio perché la visualizzazione di un'applicazione non può essere modificata all'esterno del thread dell'interfaccia utente. Il modo migliore per evitare il congelamento è utilizzare le attività asincrone con la classe AsyncTask, in questo tutorial implementeremo più thread per comprendere il comportamento dell'architettura Android.
Codice e sviluppo
Il progetto che creeremo in seguito sarà basato su un download di immagini con cui dobbiamo creare un thread che ci permetta di gestire l'accesso e il download su Internet perché il PRINCIPALE o Discussione dell'interfaccia utente non consente questa azione.
Inizieremo creando un nuovo progetto con un'attività vuota, abbiamo intitolato questo progetto "Esempio MultiThread", con una sola semplice attività creeremo la struttura del file XML che appartiene a questa attività.
Abbiamo un campo di testo, un pulsante, un layout lineare che corrisponde a una barra di caricamento indeterminata che utilizzeremo in seguito e una visualizzazione elenco che contiene una serie di URL di immagini ospitate su Internet. Nel file che contiene la classe Java per la nostra (unica) attività, è scritto con il seguente codice:
pacchetto com.omglabs.multithreaexample; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; la classe pubblica MainActivity estende AppCompatActivity implementa AdapterView.OnItemClickListener {private EditText editText; ListView privato ListView; URL privati String []; ProgressBar privato progressBar; private LinearLayout progressLayout; @Override protetto void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); editText = (EditText) findViewById (R.id.downloadURL); listView = (ListView) findViewById (R.id.listurls); listView.setOnItemClickListener (questo); urls = getResources().getStringArray (R.array.URLs); progressBar = (ProgressBar) findViewById (R.id.progressbar); progressLayout = (LinearLayout) findViewById (R.id.progresslayout); } public void download (View view) {} @Override public void onItemClick (AdapterView adapterView, View view, int i, long l) {editText.setText (urls [i]); }}Fino ad ora l'applicazione può essere compilata senza alcun problema, in questa classe dichiariamo le variabili:
- modifica il testo
- visualizzazione elenco
- URL
- barra di avanzamento
- progressoLayout
Un campo di testo, un elenco, una disposizione di stringhe, una barra di avanzamento e un layout lineare.
Nel metodo onCreate Assegniamo a questi la rispettiva vista che gli appartiene e che sono stati creati nel file XML dell'attività, ad eccezione degli url che assegna i suoi valori dalla cartella dei valori nel file di stringhe e la cui disposizione è dichiarata come segue:
http://www.fmdos.cl/wp-content/uploads/2016/03/1.jpg.webp http://vignette3.wikia.nocookie.net/teenwolf/images/9/90/Crystal_Reed_003.jpeg.webp https: // pbs.twimg.com/profile_images/699667844129107968/EvhTFBHN.jpg.webp http://vignette1.wikia.nocookie.net/teen-wolf-pack/images/0/0b/Holland-holland-roden-31699868-500-600.png.webpIl download del metodo vuoto (View view) verrà riempito con il codice che eseguirà il download e che è collegato al Pulsante di download Bot tramite l'attributo onclick. Infine il metodo sull'elementoclick che appartiene a visualizzazione elenco, riempie il campo di testo quando si fa clic su uno qualsiasi degli URL contenuti nell'elenco. Una volta compilato questo codice sarà simile a questo:
Nel passaggio successivo creeremo i metodi che procederanno al download, seguendo questi passaggi:
- Crea un oggetto della classe URL (java.net) che rappresenterà l'url da scaricare.
- Apri la connessione usando quell'oggetto.
- Leggere i dati (via web) utilizzando la classe del flusso di input in un array di byte.
- Apri / crea un file di flusso di output in cui i dati degli URL verranno salvati sulla scheda SD.
- Scrivi i dati in quel file.
- E infine chiudi la connessione.
Per ora sarà simile a questo:
download booleano pubblico usingThreads (String link) {conferma booleana = false; URL downloadLink = nullo; HttpURLConnection conne = null; InputStream inputStream = null; prova {downloadLink = nuovo URL (link); connection = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } infine {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {prova {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} conferma di restituzione; }Questo metodo che abbiamo costruito avrà solo bisogno di un Corda quale sarà l'URL da scaricare, è booleano Per confermare il download, downloadLink è l'oggetto URL, connection è la connessione che verrà effettuata per accedere all'oggetto e inputStream è quella che procederà alla lettura dei dati, se proviamo ad utilizzare questo metodo sul pulsante scarica Bot l'applicazione si interrompeva a causa dell'impossibilità di eseguire il thread principale.
Qui andiamo con l'uso dei thread, ci sono due modi per farlo con una classe ed è estendendo quella classe a Thread o implementando la classe Runnable, questa classe non è un thread ti consente semplicemente di creare un metodo che tu può essere eseguito in un momento specifico e se crei un thread separato eseguilo al suo interno.
All'interno del pulsante di download scriveremo questo codice e sarà simile a questo:
download pubblico void (vista Visualizza) {Thread mThread = nuovo thread (nuovo mRunn ()); mThread.start (); }Qui stiamo creando un nuovo thread che necessita di un oggetto Runnable che creiamo in una classe privata come questa:
classe privata mRunn implementa Runnable {@Override public void run () {download usingThreads (urls [0]); }}Crea una classe privata
NotaRicorda che questo è tutto nella classe Java della nostra unica attività.
Con la linea:
scarica usandoThreads (url [0]);Stiamo chiamando la funzione che abbiamo creato dove abbiamo aperto la connessione, ad essa viene passato un elemento dell'array URL in modo che possa leggere i dati da quell'indirizzo. Successivamente verrà modificato.
Se provassimo a eseguire questa applicazione premendo il pulsante, l'applicazione si fermerebbe, perché abbiamo bisogno di un permesso speciale per accedere a Internet, che viene richiesto tramite il manifest della nostra applicazione. Aggiungendo la riga, prima dell'etichetta:
Ora per verificare che l'applicazione esegua effettivamente il download aggiungeremo alcune righe di codice al file metodo di download utilizzando Threads, sarà simile a questo:
download booleano pubblico usingThreads (String link) {conferma booleana = false; URL downloadLink = nullo; HttpURLConnection conne = null; InputStream inputStream = null; FileOutputStream archOutputStream = nullo; File file = nullo; prova {downloadLink = nuovo URL (link); connection = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); file = new File (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (link) .getLastPathSegment ()); archOutputStream = nuovo FileOutputStream (file); int Lettura = -1; byte [] buffer = nuovo byte [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } conferma = vero; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } infine {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {prova {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }} if (archOutputStream! = null) {prova {archOutputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} conferma di restituzione; } FileOutputStream archOutputStream = nullo; File file = nullo;Le dichiarazioni di questi oggetti rappresentano la scrittura del file che si sta leggendo, e il file vuoto dove verrà salvata la lettura.
file = new File (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = nuovo FileOutputStream (file); int Lettura = -1; byte [] buffer = nuovo byte [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } conferma = vero;"File" è l'oggetto File vuoto il cui indirizzo viene costruito accedendo alla scheda SD "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" e aggiungendo una barra "/" e l'ultimo segmento dell'URL che generalmente rappresenta il nome del file a download , lo otteniamo con il metodo getLastPathSegment().
Prima di testare l'applicazione aggiungeremo un'ultima autorizzazione nel manifest:
Dopo aver eseguito l'applicazione sull'emulatore o sul dispositivo Android, alla pressione del pulsante vedremo che apparentemente non succede nulla, ma se controlliamo la cartella Download con un esploratore di file ci accorgeremo che la prima voce della lista è stata scaricata; una foto chiamata 1.jpg.webp.
Per farlo applicazione dinamica e implementare gli URL della listview, aggiorneremo il metodo di download (Visualizza vista) e aggiungeremo questo, come prima riga:
String link = editText.getText().ToString ();E nel mRunn class aggiungeremo questo, prima del metodo run():
classe privata mRunn implementa Runnable {collegamento stringa privato; mRunn pubblico (String link) {this.link = link; } @Override public void run () {download usingThreads (link); }}E nel classe mRunn aggiungeremo questo, prima del metodo run():
Quindi possiamo passare la variabile di collegamento dal campo di testo al metodo che esegue il download. L'applicazione a questo punto è perfettamente funzionante, anche se manca un po' di facilità d'uso, quindi cercheremo di risolvere il problema, utilizzando la barra di avanzamento che abbiamo dichiarato all'inizio.
Nella classe mRunn del metodo run() includeremo:
MainActivity.this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}});Prima della chiamata al downloadusandoThreads. Questo farà apparire la barra di caricamento quando premiamo il pulsante, nella clausola finalmente del download metodo usingThreads.
Aggiungeremo:
this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}});Quindi, quando il download è completo, la barra scompare di nuovo. Ciò si verificherà indipendentemente dal successo del download.
E questo è stato tutto, uno breve implementazione di più threadQuesto è un po' noioso e comporta alcune complicazioni per applicazioni più complesse.Il modo più efficace per eseguire questa operazione, nel nostro caso il download di alcune immagini, è utilizzare il AsyncTask.