
Tantissime funzionalità delle app mobile richiedono l’interazione con la Rete ed è proprio per tale motivo che il mondo Android dispone di molte librerie dedicate a questo tipo di attività (Volley, OkHttp, Retrofit ed altre ancora) ma per lo scaricamento di file di grandi dimensioni con protocollo HTTP si può ricorrere ad un servizio di sistema già pronto: il DownloadManager. Stiamo parlando del servizio chiamato in causa ogniqualvolta l’utente vuole scaricare sul dispositivo file di dimensioni considerevoli: l’andamento di tutta l’attività viene illustrato mediante notifiche status bar. Il vantaggio principale di questo meccanismo risiede nel completamento del download che viene portanto a termine superando tutte le situazioni possibili: fallimenti, disconnessioni e riavii del sistema.
Come funziona il DownloadManager
Per lavorare con il DownloadManager dobbiamo, per prima cosa, recuperare un’istanza del servizio:
DownloadManager manager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
Prepariamo poi una richiesta – classe Request – che specifica l’indirizzo del file da scaricare (fornito con un oggetto di classe Uri) ed altri dettagli come la cartella locale di salvataggio, la durata della notifica ed altro ancora
Uri uri=Uri.parse("http://www.miosito.it/file-grande-da-scaricare.pdf"); DownloadManager.Request request=new DownloadManager.Request(uri); request.setTitle("File grande da scaricare"); // ulteriori configurazioni della richiesta
Infine, accodiamo la richiesta composta al DownloadManager
long download_id=manager.enqueue(request);
e da quel momento in poi lo scaricamento del file cessa di riguardare la nostra applicazione ma diventa compentenza del sistema. Il valore long ottenuto, download_id, è un riferimento univoco allo scaricamento con cui potremo indirizzare comandi al DownloadManager.
Uso di base
Vediamo subito un esempio base (il cui codice è disponibile qui) in cui sviluppiamo una semplice Activity che, al click del Floating Action Button, avvia lo scaricamento di un file PDF di alcuni megabyte di dimensione (abbiamo scelto il manuale di OrientDB). Non applicheremo alcuna personalizzazione al comportamento di default proprio perchè ci interessa vedere cosa, di suo, il DownloadManager fa.
Posizioniamo la permission INTERNET nel file AndroidManifest.xml affinchè l’app possa accedere alla rete:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="it.devapp.downloadmanagerexample"> <uses-permission android:name="android.permission.INTERNET" /> <application ... ... </application> </manifest>
Nel metodo onCreate, predisponiamo la richiesta in base all’URI e specifichiamo nome e descrizione che dovranno apparire nella notifica di scaricamento. Inoltre, richiediamo che quest’ultima non scompaia al termine del download. Tutta l’azione del DownloadManager inizia all’interno del listener legato al click del FloatingActionButton:
- accodiamo la richiesta al DownloadManager;
- conserviamo in una variabile long il riferimento univoco allo scaricamento;
- impostiamo come azione della SnackBar la cancellazione dello scaricamento in corso, indicandolo semplicemente con il codice univoco di cui al punto precedente.
Questo il codice dell’Activity:
public class MainActivity extends AppCompatActivity { private Uri uri; private DownloadManager.Request request; private DownloadManager manager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // recuperiamo un riferimento al servizio manager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE); // definiamo l'URI da cui scaricare il file uri=Uri.parse("http://orientdb.com/docs/2.1/OrientDB-Manual.pdf"); // Prepariamo la richiesta request=new DownloadManager.Request(uri); // titolo e descrizione che appariranno nella notifica request.setTitle("Manuale di OrientDB"); request.setDescription("File PDF"); // al termine dello scaricamento la notifica non scomparirà request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // riferimento univoco al download final long download_id=manager.enqueue(request); Snackbar.make(view, "Download avviato...", Snackbar.LENGTH_LONG) .setAction("Annulla", new View.OnClickListener() { @Override public void onClick(View view) { // cliccando sull'Action otteniamo la cancellazione del download manager.remove(download_id); } }).show(); } }); } }
Questa è la SnackBar che apparirà all’avvio del download:
e questa la notifica che apparirà al termine
Il download eseguito risulterà tra quelli effettuati dal sistema tramite l’apposita app “Downloads” mentre, terminato lo scaricamento, potremo aprire il documento: trattandosi di PDF, verrà offerta la possibilità di farlo con una delle applicazioni installate.
Personalizzazione del comportamento
Oltre al comportamento standard, si può personalizzare le reazioni del DownloadManager nei momenti cruciali come il click sulla notifica (anche durante lo scaricamento) e al termine del download.
Questi due eventi verranno comunicati attraverso il sistema con due Intent corrispondenti alle costanti DownloadManager.ACTION_DOWNLOAD_COMPLETE e DownloadManager.ACTION_NOTIFICATION_CLICKED.
Per intercettare questi eventi si può usare una componente delle applicazioni Android appositamente dedicata, il BroadcastReceiver, che va implementato nel progetto come classe Java e segnalato nel file AndroidManifest.xml mediante un blocco come il seguente (all’interno del nodo <application>):
<receiver android:name=".OnCompleteReceiver" android:exported="true" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/> </intent-filter> </receiver>
L’effetto del BroadcastReceiver è puramente di test consistendo nella sola visualizzazione di un Toast eppure dimostra come esso venga attivato dall’evento anche nel caso in cui l’Activity sia stata già chiusa.
public class OnCompleteReceiver extends BroadcastReceiver { public OnCompleteReceiver() { } @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Fine dello scaricamento!!", Toast.LENGTH_LONG).show(); } }
Il BroadcastReceiver – ricordiamo – si presta alla gestione di moltissimi altri tipi di eventi e richiede la sola implementazione del metodo onReceive per gestire la situazione. Questo metodo lavora sul thread principale dell’applicazione pertanto, in caso di attività prolungate, è opportuno avviare un Service dedicato al loro svolgimento in modalità asincrona.
Dati relativi al DownloadManager
E’ inoltre interessante considerare che i dati relativi ai nostri scaricamenti sono disponibili tramite un Content Provider che il DownloadManager mette a disposizione. Tramite questo potremo sapere quali attività di scaricamento la nostra applicazione ha disposto, il loro stato, le dimensioni, titolo e descrizione applicati ed altro ancora.
Per avere tali informazioni, si dovrà utilizzare un oggetto DownloadManager.Query il quale con il metodo query fornirà un Cursor che potrà essere letto ed impiegato come siamo soliti fare nell’interazione con database:
DownloadManager manager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE); ... ... DownloadManager.Query query=new DownloadManager.Query(); ... ... Cursor c=manager.query(query);
I campi utilizzabili tramite il Cursor sono disponibili come costanti nel DownloadManager ed hanno nomi che iniziano con il prefisso “COLUMN_”. Ad esempio, avremo COLUMN_TITLE per il titolo del download, COLUMN_ID per recuperare l’identificativo del download (che abbiamo trovato in precedenza nel codice Java sotto forma di variabile long), COLUMN_LAST_MODIFIED_TIMESTAMP per l’informazione temporale dell’ultima modifica in formato timestamp o COLUMN_TOTALE_SIZE_BYTES per le dimensioni misurate in byte: la documentazione ufficiale sarà un buon riferimento per ulteriori informazioni.
Altro aspetto interessante sarà la presenza, anche in questo caso nella classe Query, di due metodi, setFilterById e setFilterByStatus, che permetteranno di raffinare la ricerca concentrandosi, rispettivamente, su uno o più id di download o su uno stato specifico dello scaricamento.
Conclusioni
Osservare come il DownloadManager lavora ci offre due spunti importanti. Innanzitutto che per interagire con la rete non esiste un solo strumento ma una moltitudine, offerti da Android o da realtà di terze parti ma sempre focalizzati su aspetti diversi: in base al lavoro da svolgere dovremo scegliere quello più adatto. In secondo luogo, si può notare che quando esiste un servizio di sistema per le operazioni che dobbiamo svolgere è sempre meglio delegarle a questi guadagnando per la nostra app efficienza ed ordine architetturale.
No Responses to “Android: scaricare grandi file con il DownloadManager”