Android Múltiples pausas de descarga reanudar en listview con actualización de progreso
Estoy intentando descargar varios archivos en listview con la barra de progreso. Lo que logré es, puedo iniciar una descarga en particular, pausar / reanudar con AsyncTask y la barra de progreso se actualiza (para un solo archivo), esta parte funciona bien
Mi problema es que no soy capaz de descargar múltiples archivos simultáneamente y cuando dejo el listview a otra pantalla, aunque mi descarga está pasando en segundo plano, pero el progreso no se actualiza, la barra de progreso muestra 0 progreso como si no se descarga, pero ha sido Descarga en segundo plano.
- MediaPlayer java.io.IOException: Prepare failed .: status = 0x1 con Huawei ALE-L21
- Descargar un archivo sin extensión desde un servidor
- Mostrar el valor del progreso para descargar volley file
- Recurso para descargar temas / estilos de aplicación de Android?
- Descarga de archivos en Android
- Cómo guardar el archivo desde el sitio web a sdcard
- Cómo mezclar / superponer dos archivos de audio mp3 en un archivo mp3 (no concatenar)
- Android: "Fin de secuencia inesperada" excepción descargar archivos grandes
- Android - Obtener el nombre de archivo original para una descarga asíncrona
- La mejor manera de descargar un archivo en una aplicación de Android
- Filtro de intenciones para descargar adjuntos de aplicaciones de gmail en Android
- Descargar imágenes con AsyncTask
- Facebook SDK para la descarga de Android siempre falla
Finalmente encontré la respuesta que era mucho más simple de lo que pensaba, aquí es como sigue
- Crear un
service
conAsynctask
para la descarga yhashtable
de valores (url, Asynctask) - Pase el valor (url, Asynctask) cuando se hace clic en un elemento de lista y compruebe si ese hashtable contiene el valor ya si sí cancela esa tarea de Asynctask si no lo agrega a hashtable e inicia Asynctask
- Ahora para actualizar el progreso en mi
adapter
corrí un hilo que iterar sobrehashtable
y pasa el valor usandoBroadcastListener
. - Y en actividad interceptar la
broadcast
y dependiendo delListItem
visible actualizar el progreso
PS: Si alguien necesita algún código puedo proporcionar el código básico de la descripción explicada anteriormente
public class DownloadingService extends Service { public static String PROGRESS_UPDATE_ACTION = DownloadingService.class.getName() + ".progress"; private static final long INTERVAL_BROADCAST = 800; private long mLastUpdate = 0; private Hashtable<String, DownloadFile> downloadTable; private LocalBroadcastManager broadcastManager; @Override public int onStartCommand(Intent intent, int flags, int startId) { MessageEntity entityRecieved = (MessageEntity) intent.getSerializableExtra("ENTITY"); queueDownload(entityRecieved); return super.onStartCommand(intent, flags, startId); } private void queueDownload(MessageEntity entityRecieved){ if (downloadTable.containsKey(entityRecieved.getPacketID())) { DownloadFile downloadFile = downloadTable.get(entityRecieved.getPacketID()); if (downloadFile.isCancelled()) { downloadFile = new DownloadFile(entityRecieved); downloadTable.put(entityRecieved.getPacketID(), downloadFile); startDownloadFileTask(downloadFile); } else { downloadFile.cancel(true); downloadTable.remove(entityRecieved.getPacketID()); } } else { DownloadFile downloadFile = new DownloadFile(entityRecieved); downloadTable.put(entityRecieved.getPacketID(), downloadFile); startDownloadFileTask(downloadFile); } } @Override public void onCreate() { super.onCreate(); downloadTable = new Hashtable<String, DownloadFile>(); broadcastManager = LocalBroadcastManager.getInstance(this); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) void startDownloadFileTask(DownloadFile asyncTask) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); else asyncTask.execute(); } private void publishCurrentProgressOneShot(boolean forced) { if (forced || System.currentTimeMillis() - mLastUpdate > INTERVAL_BROADCAST) { mLastUpdate = System.currentTimeMillis(); int[] progresses = new int[downloadTable.size()]; String[] packetIds = new String[downloadTable.size()]; int index = 0; Enumeration<String> enumKey = downloadTable.keys(); while (enumKey.hasMoreElements()) { String key = enumKey.nextElement(); int val = downloadTable.get(key).progress; progresses[index] = val; packetIds[index++] = key; } Intent i = new Intent(); i.setAction(PROGRESS_UPDATE_ACTION); i.putExtra("packetIds", packetIds); i.putExtra("progress", progresses); mBroadcastManager.sendBroadcast(i); } class DownloadFile extends AsyncTask<Void, Integer, Void> { private MessageEntity entity; private File file; private int progress; public DownloadFile(MessageEntity entity) { this.entity = entity; } @Override protected Void doInBackground(Void... arg0) { String filename = entity.getMediaURL().substring(entity.getMediaURL().lastIndexOf('/') + 1); file = new File(FileUtil.getAppStorageDir().getPath(), filename); downloadFile(entity.getMediaURL(), file); return null; } public String downloadFile(String download_file_path, File file) { int downloadedSize = 0; int totalSize = 0; try { // download the file here while ((bufferLength = inputStream.read(buffer)) > 0 && !isCancelled()) { progress = percentage; publishCurrentProgressOneShot(true); } } catch (final Exception e) { return null; } return file.getPath(); } }
En el problema grande con AsynchTask es cuando usted termina su actividad, AsynchTask pierde su pista con su UI. Después de eso, cuando regreses a esa actividad, la barra de progreso no se actualiza aunque el progreso de la descarga siga funcionando en segundo plano. De hecho, AsynchTask no pertenece a la nueva Actividad que almorzaste, por lo que la nueva instancia de la barra de progreso en la nueva Actividad no se actualizará. Para solucionar este problema te sugiero:
1- Ejecute un subproceso con un timerTask en onResume () que actualiza su barra de progreso con valores que se actualizan desde el fondo de ejecución de AsyncTask. Algo como esto:
private void updateProgressBar(){ Runnable runnable = new updateProgress(); background = new Thread(runnable); background.start(); } public class updateProgress implements Runnable { public void run() { while(Thread.currentThread()==background) try { Thread.sleep(1000); Message msg = new Message(); progress = getProgressPercentage(); handler.sendMessage(msg); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { } } } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { progress.setProgress(msg.what); } };
Y cuando su actividad no es visible debe destruir el hilo:
private void destroyRunningThreads() { if(background!=null) { background.interrupt(); background=null; } }
2- Definir una variable booleana estática global. Establezca true en onPreExecute y en onPostExecute establezca en false. Se muestra que se está descargando o no, por lo que puede comprobar si la variable es igual a true, mostrar el cuadro de diálogo de la barra de progreso anterior (puede hacer algo como esto con un valor entero o matriz de enteros para mostrar la actualización Porcentaje para cada progreso de descarga).
3- La última forma en la que personalmente he usado es mostrar el progreso de la descarga en la barra de notificación y en mi vista de lista sólo muestro que se está descargando ahora o no (utilizando el 2do método con valores booleanos). De esta manera, incluso si finaliza la actividad, la barra de notificación se actualiza con el progreso de la descarga.
Cuando dejas tu actividad, la actividad que muestra asynctask muestra la barra de progreso es asesinada y por lo tanto la dosis de progressBar no se muestra más cuando vuelves a una nueva actividad porque la dosis de asíntate no es consciente de tu nueva actividad. Solución general que funcionará en cualquier caso, por ejemplo, cuando el usuario cierre la aplicación y vuelva a abrirla y quiere saber si ProgressBar separa completamente su presentación. Lo que significa que puede crear sharedPreferences o tabla de base de datos y poner su estado de su archivo en él mientras su asynctask está descargando. Por ejemplo, cada 500 milisegundos actualizar la tabla de base de datos sharedPreferences o con la cantidad descargada del tamaño total del archivo. Entonces cuando el usuario vuelve a su nueva actividad que lee desde DB o SharedPreferences para mostrar el progresoBar y actualizarlo cada ejemplo 1000 milisegundos. De esta forma, el usuario conocerá la barra de progreso incluso si cierra la aplicación y la vuelve a abrir. Sé que se necesita un poco más de trabajo, pero sin duda hace que sus usuarios sean felices.
Con el fin de leer y actualizar a tasa fija puede utilizar scheduleAtFixedRate