{"id":12699,"date":"2017-09-05T10:34:06","date_gmt":"2017-09-05T08:34:06","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=12699"},"modified":"2017-09-05T10:34:06","modified_gmt":"2017-09-05T08:34:06","slug":"android-scaricare-grandi-file-con-il-downloadmanager","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/android-scaricare-grandi-file-con-il-downloadmanager\/","title":{"rendered":"Android: scaricare grandi file con il DownloadManager"},"content":{"rendered":"<p>Tantissime funzionalit\u00e0 delle app mobile richiedono l&#8217;interazione con la Rete ed \u00e8 proprio per tale motivo\u00a0che\u00a0il mondo Android dispone di\u00a0molte librerie dedicate a questo tipo di attivit\u00e0 (Volley, OkHttp, <a href=\"http:\/\/www.devapp.it\/wordpress\/retrofit-la-libreria-che-permette-di-integrare-api-rest-nelle-proprie-app-java-android-arriva-alla-versione-2-0\/\" target=\"_blank\" rel=\"noopener\">Retrofit<\/a> ed\u00a0altre ancora) ma per lo <strong>scaricamento di file di grandi dimensioni<\/strong> con protocollo HTTP si pu\u00f2 ricorrere ad un servizio di sistema gi\u00e0 pronto: il <strong>DownloadManager<\/strong>. Stiamo parlando del servizio chiamato in causa ogniqualvolta l&#8217;utente vuole scaricare\u00a0sul dispositivo file di dimensioni considerevoli: l&#8217;andamento di tutta l&#8217;attivit\u00e0 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.<\/p>\n<h2>Come funziona il DownloadManager<\/h2>\n<p>Per lavorare con il DownloadManager dobbiamo, per prima cosa, recuperare un&#8217;istanza del servizio:<\/p>\n<pre class=\"lang:java decode:true\">DownloadManager manager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);<\/pre>\n<p>Prepariamo poi una richiesta &#8211; classe <em>Request<\/em> &#8211; che specifica l&#8217;indirizzo del file da scaricare (fornito con un oggetto di classe <em>Uri<\/em>) ed altri dettagli come la cartella locale di salvataggio, la durata della notifica ed altro ancora<\/p>\n<pre class=\"lang:java decode:true\">Uri uri=Uri.parse(\"http:\/\/www.miosito.it\/file-grande-da-scaricare.pdf\");\r\n\r\nDownloadManager.Request request=new DownloadManager.Request(uri);\r\nrequest.setTitle(\"File grande da scaricare\");\r\n\/\/ ulteriori configurazioni della richiesta<\/pre>\n<p>Infine, accodiamo\u00a0la richiesta composta al DownloadManager<\/p>\n<pre class=\"lang:java decode:true\">long download_id=manager.enqueue(request);<\/pre>\n<p>e da quel momento in poi lo scaricamento del file cessa di riguardare la nostra applicazione ma diventa\u00a0compentenza del sistema. Il valore long ottenuto, <em>download_id<\/em>, \u00e8 un <strong>riferimento univoco allo scaricamento<\/strong> con cui potremo indirizzare comandi al DownloadManager.<\/p>\n<h2>Uso di base<\/h2>\n<p>Vediamo subito un esempio base (il cui codice \u00e8 disponibile <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/DownloadManagerExample.zip\">qui<\/a>) in cui sviluppiamo una semplice Activity\u00a0che, al <em>click<\/em> del Floating Action Button, avvia\u00a0lo 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\u00e8 ci interessa\u00a0vedere cosa, di suo, il DownloadManager fa.<\/p>\n<p>Posizioniamo la <strong>permission INTERNET<\/strong> nel file AndroidManifest.xml affinch\u00e8 l&#8217;app possa accedere alla rete:<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;manifest xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\r\n    package=\"it.devapp.downloadmanagerexample\"&gt;\r\n\r\n    &lt;uses-permission android:name=\"android.permission.INTERNET\" \/&gt;\r\n\r\n    &lt;application\r\n         ...\r\n         ...\r\n    &lt;\/application&gt;\r\n&lt;\/manifest&gt;<\/pre>\n<p>Nel metodo <em>onCreate<\/em>, predisponiamo la richiesta in base all&#8217;URI e specifichiamo nome e descrizione che dovranno apparire nella notifica di scaricamento. Inoltre, richiediamo che quest&#8217;ultima non scompaia al termine del download. Tutta l&#8217;azione del DownloadManager inizia all&#8217;interno del <em>listener<\/em> legato al click del <em>FloatingActionButton<\/em>:<\/p>\n<ul>\n<li>accodiamo la richiesta al DownloadManager;<\/li>\n<li>conserviamo in una variabile long il riferimento univoco allo scaricamento;<\/li>\n<li>impostiamo come azione della SnackBar la cancellazione dello scaricamento in corso, indicandolo semplicemente con il codice univoco di cui al punto precedente.<\/li>\n<\/ul>\n<p>Questo il codice dell&#8217;Activity:<\/p>\n<pre class=\"lang:java decode:true\">public class MainActivity extends AppCompatActivity {\r\n\r\n    private Uri uri;\r\n    private DownloadManager.Request request;\r\n    private DownloadManager manager;\r\n    \r\n    @Override\r\n    protected void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n        setContentView(R.layout.activity_main);\r\n\r\n        \/\/ recuperiamo un riferimento al servizio\r\n        manager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);\r\n\r\n        \/\/ definiamo l'URI da cui scaricare il file\r\n        uri=Uri.parse(\"http:\/\/orientdb.com\/docs\/2.1\/OrientDB-Manual.pdf\");\r\n\r\n        \/\/ Prepariamo la richiesta\r\n        request=new DownloadManager.Request(uri);\r\n\r\n        \/\/ titolo e descrizione che appariranno nella notifica\r\n        request.setTitle(\"Manuale di OrientDB\");\r\n        request.setDescription(\"File PDF\");\r\n\r\n        \/\/ al termine dello scaricamento la notifica non scomparir\u00e0\r\n        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\r\n\r\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\r\n        setSupportActionBar(toolbar);\r\n\r\n        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);\r\n        fab.setOnClickListener(new View.OnClickListener() {\r\n            @Override\r\n            public void onClick(View view) {\r\n\r\n            \/\/ riferimento univoco al download\r\n            final long download_id=manager.enqueue(request);\r\n                \r\n            Snackbar.make(view, \"Download avviato...\", Snackbar.LENGTH_LONG)\r\n                    .setAction(\"Annulla\", new View.OnClickListener() {\r\n                        @Override\r\n                        public void onClick(View view) {\r\n\r\n                            \/\/ cliccando sull'Action otteniamo la cancellazione del download\r\n                            manager.remove(download_id);\r\n                        }\r\n                    }).show();\r\n            }\r\n        });\r\n    }\r\n\r\n}<\/pre>\n<p>Questa\u00a0\u00e8\u00a0la SnackBar che apparir\u00e0 all&#8217;avvio del download:<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_01.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-12703\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_01-300x166.jpg\" alt=\"\" width=\"300\" height=\"166\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_01-300x166.jpg 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_01.jpg 550w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>e questa la notifica che apparir\u00e0 al termine<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_02.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-12702\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_02-300x160.jpg\" alt=\"\" width=\"300\" height=\"160\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_02-300x160.jpg 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/12\/android-downloadmanager-download-http-systemservice_02.jpg 550w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Il download eseguito risulter\u00e0 tra quelli effettuati dal sistema tramite l&#8217;apposita app &#8220;Downloads&#8221; mentre, terminato lo scaricamento,\u00a0potremo aprire il documento: trattandosi di PDF, verr\u00e0 offerta la possibilit\u00e0 di farlo con una delle applicazioni installate.<\/p>\n<h2>Personalizzazione del comportamento<\/h2>\n<p>Oltre al comportamento standard, si pu\u00f2 personalizzare\u00a0le\u00a0reazioni del DownloadManager nei momenti cruciali come il click sulla notifica (anche durante lo scaricamento) e al termine del download.<\/p>\n<p>Questi due eventi verranno comunicati attraverso il sistema con due <strong>Intent<\/strong> corrispondenti alle costanti DownloadManager.ACTION_DOWNLOAD_COMPLETE e\u00a0DownloadManager.ACTION_NOTIFICATION_CLICKED.<\/p>\n<p>Per intercettare questi eventi si pu\u00f2 usare una componente delle applicazioni Android appositamente dedicata, il <strong>BroadcastReceiver,<\/strong> che va\u00a0implementato\u00a0nel progetto come classe Java e segnalato nel file AndroidManifest.xml mediante un blocco come il seguente (all&#8217;interno del nodo &lt;application&gt;):<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;receiver android:name=\".OnCompleteReceiver\"\r\n            android:exported=\"true\"\r\n            android:enabled=\"true\"&gt;\r\n            &lt;intent-filter&gt;\r\n                &lt;action android:name=\"android.intent.action.DOWNLOAD_COMPLETE\"\/&gt;\r\n            &lt;\/intent-filter&gt;\r\n&lt;\/receiver&gt;<\/pre>\n<p>L&#8217;effetto del BroadcastReceiver \u00e8 puramente di test consistendo nella sola visualizzazione di un Toast eppure\u00a0dimostra come esso venga attivato dall&#8217;evento anche nel caso in cui\u00a0l&#8217;Activity sia stata gi\u00e0 chiusa.<\/p>\n<pre class=\"lang:java decode:true\">public class OnCompleteReceiver extends BroadcastReceiver {\r\n    public OnCompleteReceiver() {\r\n    }\r\n\r\n    @Override\r\n    public void onReceive(Context context, Intent intent) {\r\n        Toast.makeText(context, \"Fine dello scaricamento!!\", Toast.LENGTH_LONG).show();\r\n    }\r\n}<\/pre>\n<p>Il BroadcastReceiver &#8211; ricordiamo &#8211; si presta alla gestione di moltissimi altri tipi di eventi e richiede la sola implementazione del metodo <em>onReceive<\/em> per gestire la situazione. Questo metodo lavora sul thread principale dell&#8217;applicazione pertanto,\u00a0in caso di attivit\u00e0 prolungate, \u00e8 opportuno\u00a0avviare un Service dedicato al loro svolgimento in modalit\u00e0 asincrona.<\/p>\n<h2>Dati relativi al DownloadManager<\/h2>\n<p>E&#8217; inoltre interessante considerare che i dati relativi ai nostri scaricamenti sono disponibili tramite un <strong>Content Provider<\/strong> che il DownloadManager mette a disposizione. Tramite questo potremo sapere quali attivit\u00e0 di scaricamento la nostra applicazione ha disposto,\u00a0il loro stato, le dimensioni, titolo e descrizione applicati ed altro ancora.<\/p>\n<p>Per avere tali informazioni, si dovr\u00e0 utilizzare un oggetto <strong>DownloadManager.Query<\/strong> il quale con il metodo <em>query<\/em> fornir\u00e0 un <em>Cursor<\/em> che potr\u00e0 essere letto ed impiegato come siamo soliti fare nell&#8217;interazione con database:<\/p>\n<pre class=\"lang:java decode:true\">DownloadManager manager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);\r\n...\r\n...\r\nDownloadManager.Query query=new DownloadManager.Query();\r\n...\r\n...\r\nCursor c=manager.query(query);<\/pre>\n<p>I<strong> campi utilizzabili tramite il Cursor<\/strong> sono disponibili come costanti nel DownloadManager ed hanno nomi che iniziano con il prefisso &#8220;COLUMN_&#8221;. Ad esempio, avremo COLUMN_TITLE per il titolo del download,\u00a0COLUMN_ID per recuperare l&#8217;identificativo del download (che abbiamo trovato in precedenza nel codice Java sotto forma di variabile long),\u00a0COLUMN_LAST_MODIFIED_TIMESTAMP per l&#8217;informazione temporale dell&#8217;ultima modifica in formato timestamp o\u00a0COLUMN_TOTALE_SIZE_BYTES per le dimensioni misurate in byte: la documentazione ufficiale sar\u00e0 un buon riferimento per ulteriori informazioni.<\/p>\n<p>Altro aspetto interessante sar\u00e0 la presenza, anche in questo caso nella classe Query, di due metodi, <em>setFilterById<\/em> e <em>setFilterByStatus<\/em>, che permetteranno di raffinare la ricerca concentrandosi, rispettivamente, su uno o pi\u00f9 id di download o su uno stato specifico dello scaricamento.<\/p>\n<h2>Conclusioni<\/h2>\n<p>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,\u00a0offerti da Android o\u00a0da realt\u00e0 di\u00a0terze parti ma sempre focalizzati su aspetti diversi: in base al lavoro da svolgere dovremo scegliere quello pi\u00f9 adatto. In secondo luogo, si pu\u00f2 notare che quando esiste un servizio di sistema per le operazioni che dobbiamo svolgere \u00e8 sempre meglio delegarle a questi guadagnando per la nostra app efficienza ed ordine architetturale.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tantissime funzionalit\u00e0 delle app mobile richiedono l&#8217;interazione con la Rete ed \u00e8 proprio per tale motivo\u00a0che\u00a0il mondo&#8230;<\/p>\n","protected":false},"author":561,"featured_media":13222,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1682,1],"tags":[1278,1837,181,1836,1527],"class_list":["post-12699","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-tutorial-pratici","tag-android","tag-android-system-service","tag-download","tag-downloadmanager","tag-http"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12699","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/561"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=12699"}],"version-history":[{"count":6,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12699\/revisions"}],"predecessor-version":[{"id":13233,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12699\/revisions\/13233"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media\/13222"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=12699"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=12699"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=12699"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}