Android
AsyncTask
Ricerca…
Parametri
Parametro | Dettagli |
---|---|
Parametri | il tipo di parametri inviati all'attività al momento dell'esecuzione. |
Progresso | il tipo di unità di progresso pubblicate durante il calcolo dello sfondo |
Risultato | il tipo del risultato del calcolo dello sfondo. |
Uso di base
In Attività e servizi Android, la maggior parte dei callback vengono eseguiti sul thread principale . Ciò semplifica l'aggiornamento dell'interfaccia utente, ma l'esecuzione di attività gravose di processore o I / O sul thread principale può causare la sospensione dell'interfaccia utente e la mancata risposta ( documentazione ufficiale su ciò che accade).
Puoi rimediare mettendo queste attività più pesanti su un thread in background.
Un modo per farlo è usare AsyncTask , che fornisce un framework per facilitare l'uso di un thread in background e anche eseguire attività Thread UI prima, durante e dopo lo sfondo Thread ha completato il suo lavoro.
Metodi che possono essere sovrascritti durante l'estensione di AsyncTask
:
-
onPreExecute()
: richiamato sul thread UI primaonPreExecute()
-
doInBackground()
: richiamato sul thread in background immediatamente dopo cheonPreExecute()
termina l'esecuzione. -
onProgressUpdate()
: richiamato sul thread dell'interfaccia utente dopo una chiamata apublishProgress(Progress...)
. -
onPostExecute()
: richiamato sul thread dell'interfaccia utente al termine del calcolo dello sfondo
Esempio
public class MyCustomAsyncTask extends AsyncTask<File, Void, String> {
@Override
protected void onPreExecute(){
// This runs on the UI thread before the background thread executes.
super.onPreExecute();
// Do pre-thread tasks such as initializing variables.
Log.v("myBackgroundTask", "Starting Background Task");
}
@Override
protected String doInBackground(File... params) {
// Disk-intensive work. This runs on a background thread.
// Search through a file for the first line that contains "Hello", and return
// that line.
try (Scanner scanner = new Scanner(params[0])) {
while (scanner.hasNextLine()) {
final String line = scanner.nextLine();
publishProgress(); // tell the UI thread we made progress
if (line.contains("Hello")) {
return line;
}
}
return null;
}
}
@Override
protected void onProgressUpdate(Void...p) {
// Runs on the UI thread after publishProgress is invoked
Log.v("Read another line!")
}
@Override
protected void onPostExecute(String s) {
// This runs on the UI thread after complete execution of the doInBackground() method
// This function receives result(String s) returned from the doInBackground() method.
// Update UI with the found string.
TextView view = (TextView) findViewById(R.id.found_string);
if (s != null) {
view.setText(s);
} else {
view.setText("Match not found.");
}
}
}
Uso:
MyCustomAsyncTask asyncTask = new MyCustomAsyncTask<File, Void, String>();
// Run the task with a user supplied filename.
asyncTask.execute(userSuppliedFilename);
o semplicemente:
new MyCustomAsyncTask().execute(userSuppliedFilename);
Nota
Quando AsyncTask
un AsyncTask
possiamo passare tre tipi tra parentesi < >
.
Definito come <Params, Progress, Result>
(vedere la sezione Parametri )
Nell'esempio precedente abbiamo utilizzato i tipi <File, Void, String>
:
AsyncTask<File, Void, String>
// Params has type File
// Progress has unused type
// Result has type String
Void
viene utilizzato quando si desidera contrassegnare un tipo come non usato.
Si noti che non è possibile passare i tipi primitivi (cioè int
, float
e altri 6) come parametri. In questi casi, dovresti passare le loro classi wrapper , ad esempio Integer
anziché int
, o Float
invece di float
.
Il ciclo di vita AsyncTask e Activity
AsyncTasks non segue il ciclo di vita delle istanze di attività. Se si avvia un AsyncTask all'interno di un'attività e si ruota il dispositivo, l'attività verrà distrutta e verrà creata una nuova istanza. Ma AsyncTask non morirà. Continuerà a vivere fino al suo completamento.
Soluzione: AsyncTaskLoader
Una sottoclasse di Caricatori è AsyncTaskLoader. Questa classe svolge la stessa funzione dell'AsyncTask, ma molto meglio. Può gestire le modifiche alla configurazione delle attività più facilmente e si comporta nei cicli di vita di Fragments and Activities. La cosa bella è che AsyncTaskLoader può essere utilizzato in qualsiasi situazione in cui viene utilizzato AsyncTask. Ogni volta che i dati devono essere caricati in memoria per l'attività / frammento da gestire, AsyncTaskLoader può fare meglio il lavoro.
Annullare AsyncTask
YourAsyncTask task = new YourAsyncTask();
task.execute();
task.cancel();
Questo non ferma il tuo compito se fosse in corso, imposta semplicemente il flag cancellato che può essere controllato controllando il valore restituito da isCancelled()
(assumendo che il tuo codice sia correntemente in esecuzione) facendo così:
class YourAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
while(!isCancelled()) {
... doing long task stuff
//Do something, you need, upload part of file, for example
if (isCancelled()) {
return null; // Task was detected as canceled
}
if (yourTaskCompleted) {
return null;
}
}
}
}
Nota
Se un AsyncTask viene cancellato mentre doInBackground(Params... params)
è ancora in esecuzione, allora il metodo onPostExecute(Result result)
NON verrà chiamato dopo il doInBackground(Params... params)
. AsyncTask richiamerà invece onCancelled(Result result)
per indicare che l'attività è stata annullata durante l'esecuzione.
Pubblicazione dei progressi
A volte, abbiamo bisogno di aggiornare il progresso del calcolo fatto da un AsyncTask
. Questo progresso potrebbe essere rappresentato da una stringa, un intero, ecc. Per fare ciò, dobbiamo usare due funzioni. Per prima cosa, dobbiamo impostare la funzione onProgressUpdate
cui tipo di parametro è uguale al secondo parametro di tipo del nostro AsyncTask
.
class YourAsyncTask extends AsyncTask<URL, Integer, Long> {
@Override
protected void onProgressUpdate(Integer... args) {
setProgressPercent(args[0])
}
}
In secondo luogo, dobbiamo usare la funzione publishProgress
necessariamente sulla funzione doInBackground
, e questo è tutto, il metodo precedente farà tutto il lavoro.
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
Scarica immagine usando AsyncTask in Android
Questo tutorial spiega come scaricare l'immagine usando AsyncTask in Android. L'esempio seguente scarica l'immagine mentre mostri la barra di avanzamento durante il download.
Comprendere Android AsyncTask
L'attività asincrona consente di implementare MultiThreading senza sporcare le mani nei thread. AsyncTask consente l'uso corretto e semplice del thread dell'interfaccia utente. Permette di eseguire operazioni in background e di passare i risultati sul thread dell'interfaccia utente. Se stai facendo qualcosa di isolato relativo all'interfaccia utente, ad esempio il download di dati da presentare in un elenco, andare avanti e utilizzare AsyncTask.- AsyncTasks dovrebbe idealmente essere utilizzato per operazioni brevi (pochi secondi al massimo).
- Un'attività asincrona è definita da 3 tipi generici, denominati Params, Progress e Result e 4 passaggi, chiamati
onPreExecute()
,doInBackground()
,onProgressUpdate()
eonPostExecute()
. - In
onPreExecute()
è possibile definire il codice, che deve essere eseguito prima dell'inizio dell'elaborazione in background. - doInBackground ha un codice che deve essere eseguito in background, qui in
doInBackground()
possiamo inviare i risultati più volte al thread degli eventi con il metodo publishProgress (), per notificare che l'elaborazione in background è stata completata possiamo restituire risultati semplicemente. -
onProgressUpdate()
metodoonProgressUpdate()
riceve gli aggiornamenti di avanzamento dal metododoInBackground()
, che viene pubblicato tramite il metodopublishProgress()
e questo metodo può utilizzare questo aggiornamento di avanzamento per aggiornare il thread di eventi -
onPostExecute()
metodoonPostExecute()
gestisce i risultati restituiti dal metododoInBackground()
. - I tipi generici usati sono
- Params, il tipo di parametri inviati all'attività dopo l'esecuzione
- Progresso, il tipo di unità di progresso pubblicate durante il calcolo dello sfondo.
- Risultato, il tipo del risultato del calcolo dello sfondo.
- Se un'attività asincrona non utilizza alcun tipo, può essere contrassegnata come tipo Void.
- Un'attività asincrona in esecuzione può essere annullata chiamando il metodo
cancel(boolean)
.
Download di immagini tramite Android AsyncTask
il tuo layout .xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/downloadButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click Here to Download" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="Your image will appear here" />
</LinearLayout>
.java class
package com.javatechig.droid;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class ImageDownladerActivity extends Activity {
private ImageView downloadedImg;
private ProgressDialog simpleWaitDialog;
private String downloadUrl = "http://www.9ori.com/store/media/images/8ab579a656.jpg";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.asynch);
Button imageDownloaderBtn = (Button) findViewById(R.id.downloadButton);
downloadedImg = (ImageView) findViewById(R.id.imageView);
imageDownloaderBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
new ImageDownloader().execute(downloadUrl);
}
});
}
private class ImageDownloader extends AsyncTask {
@Override
protected Bitmap doInBackground(String... param) {
// TODO Auto-generated method stub
return downloadBitmap(param[0]);
}
@Override
protected void onPreExecute() {
Log.i("Async-Example", "onPreExecute Called");
simpleWaitDialog = ProgressDialog.show(ImageDownladerActivity.this,
"Wait", "Downloading Image");
}
@Override
protected void onPostExecute(Bitmap result) {
Log.i("Async-Example", "onPostExecute Called");
downloadedImg.setImageBitmap(result);
simpleWaitDialog.dismiss();
}
private Bitmap downloadBitmap(String url) {
// initilize the default HTTP client object
final DefaultHttpClient client = new DefaultHttpClient();
//forming a HttpGet request
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
//check 200 OK for success
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode +
" while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
// getting contents from the stream
inputStream = entity.getContent();
// decoding stream data back into image Bitmap that android understands
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// You Could provide a more explicit error message for IOException
getRequest.abort();
Log.e("ImageDownloader", "Something went wrong while" +
" retrieving bitmap from " + url + e.toString());
}
return null;
}
}
}
Dato che al momento non esiste un campo di commento per gli esempi (o non l'ho trovato o non ne ho il permesso) ecco alcuni commenti a riguardo:
Questo è un buon esempio di cosa si può fare con AsyncTask.
Tuttavia, l'esempio ha attualmente problemi con
- possibili perdite di memoria
- arresto anomalo dell'app se si verificava una rotazione dello schermo poco prima del completamento dell'attività asincrona.
Per dettagli vedi:
- Passa Attività come Debolezza di Risanamento per evitare perdite di memoria
- http://stackoverflow.com/documentation/android/117/asynctask/5377/possible-problems-with-inner-async-tasks
- Evitare le perdite di attività con AsyncTask
Passa Attività come Debolezza di Risanamento per evitare perdite di memoria
È normale che un AsyncTask richieda un riferimento all'attività che lo ha chiamato.
Se AsyncTask è una classe interna dell'attività, puoi fare riferimento direttamente a qualsiasi variabile / metodo membro.
Se, tuttavia, AsyncTask non è una classe interna dell'attività, sarà necessario passare un riferimento attività a AsyncTask. Quando si esegue questa operazione, un potenziale problema che potrebbe verificarsi è che AsyncTask manterrà il riferimento dell'attività fino a quando AsyncTask non avrà completato il proprio lavoro nel suo thread in background. Se l'attività è terminata o terminata prima che venga eseguito il lavoro sul thread in background di AsyncTask, AsyncTask avrà ancora il suo riferimento all'attività e, pertanto, non può essere sottoposto a Garbage Collection.
Di conseguenza, ciò causerà una perdita di memoria.
Per evitare che ciò accada, fai uso di un WeakReference in AsyncTask invece di avere un riferimento diretto all'Attività.
Ecco un esempio AsyncTask che utilizza un WeakReference:
private class MyAsyncTask extends AsyncTask<String, Void, Void> {
private WeakReference<Activity> mActivity;
public MyAsyncTask(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
protected void onPreExecute() {
final Activity activity = mActivity.get();
if (activity != null) {
....
}
}
@Override
protected Void doInBackground(String... params) {
//Do something
String param1 = params[0];
String param2 = params[1];
return null;
}
@Override
protected void onPostExecute(Void result) {
final Activity activity = mActivity.get();
if (activity != null) {
activity.updateUI();
}
}
}
Chiamare AsyncTask da un'attività:
new MyAsyncTask(this).execute("param1", "param2");
Chiamando il AsyncTask da un frammento:
new MyAsyncTask(getActivity()).execute("param1", "param2");
Ordine di esecuzione
Quando sono stati introdotti per la prima volta, AsyncTasks
stato eseguito in serie su un singolo thread in background. A partire da DONUT
, questo è stato modificato in un pool di thread che consente a più attività di operare in parallelo. A partire da HONEYCOMB
, le attività vengono eseguite su un singolo thread per evitare errori di applicazione comuni causati dall'esecuzione parallela.
Se si desidera realmente l'esecuzione parallela, è possibile richiamare executeOnExecutor(java.util.concurrent.Executor, Object[])
con THREAD_POOL_EXECUTOR
.
SERIAL_EXECUTOR -> Un Executor che esegue le attività una alla volta in ordine seriale.
THREAD_POOL_EXECUTOR -> Un Executor che può essere utilizzato per eseguire attività in parallelo.
campione :
Task task = new Task();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, data);
else
task.execute(data);
AsyncTask: esecuzione seriale ed esecuzione parallela dell'attività
AsyncTask è una classe astratta e non eredita la classe Thread
. Ha un metodo astratto doInBackground(Params... params)
, che viene sovrascritto per eseguire l'operazione. Questo metodo è chiamato da AsyncTask.call()
.
Executor fa parte del pacchetto java.util.concurrent
.
Inoltre, AsyncTask contiene 2 Executor
THREAD_POOL_EXECUTOR
Utilizza i thread di lavoro per eseguire le attività parallelamente.
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
SERIAL_EXECUTOR
Esegue il compito in serie, cioè uno per uno.
private static class SerialExecutor implements Executor { }
Entrambi gli Executor
sono statici , quindi THREAD_POOL_EXECUTOR
solo un THREAD_POOL_EXECUTOR
e un oggetto SerialExecutor
, ma è possibile creare diversi oggetti AsyncTask
.
Pertanto, se si tenta di eseguire più attività in background con l'Executor predefinito ( SerialExecutor
), queste attività saranno SerialExecutor
ed eseguite in serie.
Se si tenta di eseguire più attività in background con THREAD_POOL_EXECUTOR
, verranno eseguite parallelamente.
Esempio:
public class MainActivity extends Activity {
private Button bt;
private int CountTask = 0;
private static final String TAG = "AsyncTaskExample";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt = (Button) findViewById(R.id.button);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BackgroundTask backgroundTask = new BackgroundTask ();
Integer data[] = { ++CountTask, null, null };
// Task Executed in thread pool ( 1 )
backgroundTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, data);
// Task executed Serially ( 2 )
// Uncomment the below code and comment the above code of Thread
// pool Executor and check
// backgroundTask.execute(data);
Log.d(TAG, "Task = " + (int) CountTask + " Task Queued");
}
});
}
private class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {
int taskNumber;
@Override
protected Integer doInBackground(Integer... integers) {
taskNumber = integers[0];
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d(TAG, "Task = " + taskNumber + " Task Running in Background");
publishProgress(taskNumber);
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Integer aLong) {
super.onPostExecute(aLong);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.d(TAG, "Task = " + (int) values[0]
+ " Task Execution Completed");
}
}
}
Esegui Fare clic sul pulsante più volte per avviare un'attività e vedere il risultato.
Attività eseguita nel pool di thread (1)
Ogni operazione richiede 1000 ms per essere completata.
A t = 36 s, le attività 2, 3 e 4 sono messe in coda e hanno iniziato l'esecuzione anche perché sono in esecuzione parallela.
08-02 19:48:35.815: D/AsyncTaskExample(11693): Task = 1 Task Queued
08-02 19:48:35.815: D/AsyncTaskExample(11693): Task = 1 Task Running in Background
08-02 19:48:**36.025**: D/AsyncTaskExample(11693): Task = 2 Task Queued
08-02 19:48:**36.025**: D/AsyncTaskExample(11693): Task = 2 Task Running in Background
08-02 19:48:**36.165**: D/AsyncTaskExample(11693): Task = 3 Task Queued
08-02 19:48:**36.165**: D/AsyncTaskExample(11693): Task = 3 Task Running in Background
08-02 19:48:**36.325**: D/AsyncTaskExample(11693): Task = 4 Task Queued
08-02 19:48:**36.325**: D/AsyncTaskExample(11693): Task = 4 Task Running in Background
08-02 19:48:**36.815**: D/AsyncTaskExample(11693): Task = 1 Task Execution Completed
08-02 19:48:**36.915**: D/AsyncTaskExample(11693): Task = 5 Task Queued
08-02 19:48:**36.915**: D/AsyncTaskExample(11693): Task = 5 Task Running in Background
08-02 19:48:37.025: D/AsyncTaskExample(11693): Task = 2 Task Execution Completed
08-02 19:48:37.165: D/AsyncTaskExample(11693): Task = 3 Task Execution Completed
----------
Commento Task Executed in thread pool
Task executed Serially
Task Executed in thread pool
(1) e uncomment Task executed Serially
(2).
Esegui Fare clic sul pulsante più volte per avviare un'attività e vedere il risultato.
Sta eseguendo l'attività in modo seriale quindi ogni attività viene avviata dopo che l'attività corrente ha completato l'esecuzione. Di conseguenza, quando l'esecuzione del task 1 viene completata, solo l'attività 2 inizia a essere eseguita in background. Vice versa.
08-02 19:42:57.505: D/AsyncTaskExample(10299): Task = 1 Task Queued
08-02 19:42:57.505: D/AsyncTaskExample(10299): Task = 1 Task Running in Background
08-02 19:42:57.675: D/AsyncTaskExample(10299): Task = 2 Task Queued
08-02 19:42:57.835: D/AsyncTaskExample(10299): Task = 3 Task Queued
08-02 19:42:58.005: D/AsyncTaskExample(10299): Task = 4 Task Queued
08-02 19:42:58.155: D/AsyncTaskExample(10299): Task = 5 Task Queued
08-02 19:42:58.505: D/AsyncTaskExample(10299): Task = 1 Task Execution Completed
08-02 19:42:58.505: D/AsyncTaskExample(10299): Task = 2 Task Running in Background
08-02 19:42:58.755: D/AsyncTaskExample(10299): Task = 6 Task Queued
08-02 19:42:59.295: D/AsyncTaskExample(10299): Task = 7 Task Queued
08-02 19:42:59.505: D/AsyncTaskExample(10299): Task = 2 Task Execution Completed
08-02 19:42:59.505: D/AsyncTaskExample(10299): Task = 3 Task Running in Background
08-02 19:43:00.035: D/AsyncTaskExample(10299): Task = 8 Task Queued
08-02 19:43:00.505: D/AsyncTaskExample(10299): Task = 3 Task Execution Completed
08-02 19:43:**00.505**: D/AsyncTaskExample(10299): Task = 4 Task Running in Background
08-02 19:43:**01.505**: D/AsyncTaskExample(10299): Task = 4 Task Execution Completed
08-02 19:43:**01.515**: D/AsyncTaskExample(10299): Task = 5 Task Running in Background
08-02 19:43:**02.515**: D/AsyncTaskExample(10299): Task = 5 Task Execution Completed
08-02 19:43:**02.515**: D/AsyncTaskExample(10299): Task = 6 Task Running in Background
08-02 19:43:**03.515**: D/AsyncTaskExample(10299): Task = 7 Task Running in Background
08-02 19:43:**03.515**: D/AsyncTaskExample(10299): Task = 6 Task Execution Completed
08-02 19:43:04.515: D/AsyncTaskExample(10299): Task = 8 Task Running in Background
08-02 19:43:**04.515**: D/AsyncTaskExample(10299): Task = 7 Task Execution Completed