• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

Come caricare i dati in modo asincrono usando i Loader nello sviluppo di applicazioni Android

By Giuseppe Maggi | on 31 Agosto 2016 | 0 Comment
Android

Sappiamo che le applicazioni realizzate per Android devono essere il più reattive possibile per massimizzare la user experience. Ciò viene realizzato con le cosiddette operazioni asincrone nelle quali un’attività che può rivelarsi più lenta viene svolta in background, su un thread secondario, una sorta di filone di esecuzione parallelo rispetto a quello in cui opera l’interfaccia utente.

Per non costringere i programmatori a dover fronteggiare una tematica ostica come i thread e le vie con cui essi comunicano con il resto dell’applicazione, Android ha messo a disposizione una classe apposita, AsyncTask, che al suo interno contiene doInBackground un metodo che lavora su un thread secondario (su cui svolgere le operazioni lente) ed altri metodi che si occupano di dialogare con il thread principale.

Tra le varie operazioni “lente”, annoveriamo tutte le forme di caricamento dati che vanno dal parsing di un file XML al JSON  fino alle query svolte su database. Per svolgere caricamenti asincroni, esiste una classe idonea, AsyncTaskLoader: la logica di funzionamento è simile a quella di AsyncTask ma in questo caso siamo coadiuvati da un componente che gestisce il nostro lavoro, il LoaderManager.

L’esempio: i dati da caricare

Per creare un Loader dobbiamo estendere la classe AsyncTaskLoader<E> specificando il tipo di risultato che verrà offerto. Noi faremo un esperimento con un Loader costruito attorno ad un database Sqlite: in pratica, quello che faremo sarà caricare i risultati di una query in background. Vale la pena ancora sottolineare che tutto ciò che faremo sarà applicabile a qualsiasi altro tipo di caricamento dati.

Prima di tutto vediamo il nostro database. Si tratta di un esempio di prova, semplicissimo, costituito da una sola tabella con due campi. Serve a memorizzare un messaggio di log in cui sarà registrato l’ora in cui si è fatto click su un pulsante.

Questo il nostro helper per gestire creazione e richiamo del database:

public class DbHelper extends SQLiteOpenHelper{

    public DbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql="CREATE TABLE messaggi (_id INTEGER PRIMARY KEY, messaggio TEXT)";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

La classe Logger invece sfrutterà l’helper per salvare dati nel database e svolgere le query:

public class Logger {

    private DbHelper helper;

    public Logger(Context ctx)
    {
        helper=new DbHelper(ctx,"LogDB",null,1);
    }

    public Cursor messaggi()
    {
        SQLiteDatabase db= helper.getReadableDatabase();
        return db.query("messaggi",null,null,null,null,null,null);
    }

    public boolean nuovoMessaggio(String s)
    {
        SQLiteDatabase db= helper.getWritableDatabase();
        ContentValues cv=new ContentValues();
        cv.put("messaggio", s);
        try {
            db.insert("messaggi", null, cv);
        }catch(SQLiteException se)
        {
            return false;
        }
        return true;
    }
}

Realizziamo il layout

Il layout sarà molto semplice: costituito da un pulsante ed una ListView ad ogni click salverà l’ora corrente su database e la lista mostrerà tutti i log salvati sinora.

Questa la sua descrizione XML (file: /res/layout/activity_main.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Aggiorna"
        android:onClick="aggiorna"/>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lista"/>

</LinearLayout>

Ed ecco il suo aspetto:

android-loader-caricamento-dati-asincrono_img_01

Loader e LoaderManager

Ora che sono stati chiariti tutti gli aspetti preliminari possiamo concentrarci sugli aspetti salienti dell’esempio. Il vantaggio di usare un Loader è che non solo permette di caricare i dati in maniera asincrona ma si lega al ciclo di vita dell’Activity (o del Fragment) in cui viene usato ed in casi come la rotazione del dispositivo ripropone i risultati dell’ultimo caricamento senza svolgere di nuovo l’operazione. Con il LoaderManager avvieremo il nostro Loader – ad esempio nell’onCreate dell’app – mentre con un oggetto listener che estende LoaderCallbacks verranno gestite le principali fasi di vita del Loader:

  • inizializzazione
  • trattamento dei risultati restituiti
  • reset

Vediamo subito come è fatto il nostro Loader:

public class DbLoader extends AsyncTaskLoader<Cursor> {

    private Logger logger;
    private Cursor results;

    public DbLoader(Context context, Logger l) {
        super(context);
        logger=l;
    }

    @Override
    protected void onStartLoading() {
        if (results != null) {
            deliverResult(results);
        }
        if (takeContentChanged() || results == null) {
            forceLoad();
        }
    }

    @Override
    public Cursor loadInBackground() {
        return logger.messaggi();
    }

    @Override
    public void deliverResult(Cursor c) {
         if (isReset()) {
             if (c != null) {
                 c.close();
             }
             return;
         }
         Cursor saved = results;
         results = c;

         if (isStarted()) {
            super.deliverResult(c);
         }

         if (saved != null && !saved.isClosed()) {
             saved.close();
         }
     }

}

Sul nostro Loader notiamo innanzitutto due cose: la classe estende AsyncTaskLoader<Cursor> con cui dichiariamo che il tipo di dato trattato è il Cursor; il metodo loadInBackground che svolge asincronamente il lavoro “pesante”, in pratica esegue la query sul database. Il Loader conserva ogni Cursor recuperato dalle query nella proprietà results e la userà come una sorta di cache.

Tale cache viene gestita, in primo luogo, nel metodo onStartLoading dove se c’è un risultato disponibile viene immediatamente restituito. Qui, si nota subito l’uso di tre metodi importanti in questo contesto: forceLoad costringe il Loader ad eseguire nuovamente il metodo loadInBackground per recuperare una nuova copia dei dati; takeContentChanged verifica se l’ultima lettura dei dati è ancora valida o è intercorsa qualche modifica al database (restituirà true se si è invocato nel frattempo onContentChanged); deliverResult è il metodo che si occupa di restituire i dati dal Loader all’Activity principale.

Proprio in deliverResult passa il Cursor ottenuto subito prima di essere restituito: in questo caso, ci preoccupiamo di salvarne un riferimento in results – per cache – e di chiudere il vecchio cursore.

Nel codice dell’Activity possiamo invece riconoscere il ruolo del LoaderManager:

public class MainActivity extends AppCompatActivity {

    private CursorAdapter adapter;
    private ListView list;
    private SimpleDateFormat format=new SimpleDateFormat("HH:mm:ss");
    private Logger logger;
    private final static int LOADER_ID=1000;

    private LoaderManager.LoaderCallbacks<Cursor> callbacks=
            new LoaderManager.LoaderCallbacks<Cursor>() {
                @Override
                public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                    return new DbLoader(MainActivity.this, logger);
                }

                @Override
                public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                    adapter.changeCursor(data);
                }

                @Override
                public void onLoaderReset(Loader<Cursor> loader) {
                    adapter.changeCursor(null);
                }
            };


    public void aggiorna(View v)
    {
        String msg="Ultimo tic: "+format.format(new Date());

        if (logger.nuovoMessaggio(msg))
            getSupportLoaderManager().getLoader(LOADER_ID).onContentChanged();

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        logger=new Logger(this);

        list= (ListView) findViewById(R.id.lista);

        adapter=new SimpleCursorAdapter(this,android.R.layout.simple_list_item_1, null,
                new String[]{"messaggio"}, new int[]{android.R.id.text1}, 0);

        list.setAdapter(adapter);

        getSupportLoaderManager().initLoader(LOADER_ID, null, callbacks);
    }


}

All’interno dell’onCreate, chiediamo al LoaderManager di inizializzare un Loader il cui caricamento ci viene notificato nel metodo onLoadFinished dei LoaderCallbacks. Ogni volta che viene cliccato il pulsante presente nell’interfaccia verrà salvato un nuovo valore e si invocherà il metodo onContentChanged per avvisare il Loader che i dati devono essere ricaricati dal database.

L’uso dei Loader può sembrare complesso, ma in realtà una volta presa confidenza con il concetto tutto appare più chiaro: si deve ricordare per lo più che in loadInBackground si fa il lavoro “pesante” ed il LoaderManager gestisce il caricamento mentre tutti gli altri metodi completano il meccanismo e ne curano l’efficienza.

Share this story:
  • tweet

Tags: android loaderAsyncTaskLoadercreare app androidprogrammazione androidTutorial Pratici

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • Android app con database interno: guida passo passo

    27 Novembre 2017 - 2 Comments
  • Come fare il login su facebook dalle applicazioni Android

    11 Ottobre 2016 - 0 Comment
  • Android Fingerprint API

    28 Giugno 2016 - 0 Comment

Author Description

No Responses to “Come caricare i dati in modo asincrono usando i Loader nello sviluppo di applicazioni Android”

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO