Descargue un archivo con Android y muestre el progreso en un ProgressDialog

1047

Estoy tratando de escribir una aplicación simple que se actualice. Para esto necesito una función simple que pueda descargar un archivo y mostrar el progreso actual en a ProgressDialog. Sé cómo hacerlo ProgressDialog, pero no estoy seguro de cómo mostrar el progreso actual y cómo descargar el archivo en primer lugar.

Tom Leese
fuente
2
Espero que el siguiente enlace pueda ser de ayuda ... androidhive.info/2012/04/…
Ganesh Katikar
1
Para una solución rápida, consulte esta respuesta
Richa
stackoverflow.com/a/43069239/3879214 revise esta respuesta
Anu Martin

Respuestas:

1874

Hay muchas formas de descargar archivos. A continuación publicaré las formas más comunes; depende de usted decidir qué método es mejor para su aplicación.

1. Use AsyncTasky muestre el progreso de la descarga en un cuadro de diálogo

Este método le permitirá ejecutar algunos procesos en segundo plano y actualizar la IU al mismo tiempo (en este caso, actualizaremos una barra de progreso).

Importaciones:

import android.os.PowerManager;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;

Este es un código de ejemplo:

// declare the dialog as a member field of your activity
ProgressDialog mProgressDialog;

// instantiate it within the onCreate method
mProgressDialog = new ProgressDialog(YourActivity.this);
mProgressDialog.setMessage("A message");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(true);

// execute this when the downloader must be fired
final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
downloadTask.execute("the url to the file you want to download");

mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {

    @Override
    public void onCancel(DialogInterface dialog) {
        downloadTask.cancel(true); //cancel the task
    }
});

El AsyncTaskse verá así:

// usually, subclasses of AsyncTask are declared inside the activity class.
// that way, you can easily modify the UI thread from here
private class DownloadTask extends AsyncTask<String, Integer, String> {

    private Context context;
    private PowerManager.WakeLock mWakeLock;

    public DownloadTask(Context context) {
        this.context = context;
    }

    @Override
    protected String doInBackground(String... sUrl) {
        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(sUrl[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();

            // expect HTTP 200 OK, so we don't mistakenly save error report
            // instead of the file
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return "Server returned HTTP " + connection.getResponseCode()
                        + " " + connection.getResponseMessage();
            }

            // this will be useful to display download percentage
            // might be -1: server did not report the length
            int fileLength = connection.getContentLength();

            // download the file
            input = connection.getInputStream();
            output = new FileOutputStream("/sdcard/file_name.extension");

            byte data[] = new byte[4096];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                // allow canceling with back button
                if (isCancelled()) {
                    input.close();
                    return null;
                }
                total += count;
                // publishing the progress....
                if (fileLength > 0) // only if total length is known
                    publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }
        } catch (Exception e) {
            return e.toString();
        } finally {
            try {
                if (output != null)
                    output.close();
                if (input != null)
                    input.close();
            } catch (IOException ignored) {
            }

            if (connection != null)
                connection.disconnect();
        }
        return null;
    }

El método anterior ( doInBackground) se ejecuta siempre en un hilo de fondo. No deberías hacer ninguna tarea de IU allí. Por otro lado, el onProgressUpdatey onPreExecuteejecutar en el hilo de la interfaz de usuario, por lo que puede cambiar la barra de progreso:

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // take CPU lock to prevent CPU from going off if the user 
        // presses the power button during download
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
             getClass().getName());
        mWakeLock.acquire();
        mProgressDialog.show();
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        // if we get here, length is known, now set indeterminate to false
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setMax(100);
        mProgressDialog.setProgress(progress[0]);
    }

    @Override
    protected void onPostExecute(String result) {
        mWakeLock.release();
        mProgressDialog.dismiss();
        if (result != null)
            Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
        else
            Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
    }

Para que esto se ejecute, necesita el permiso WAKE_LOCK.

<uses-permission android:name="android.permission.WAKE_LOCK" />

2. Descargar del servicio

La gran pregunta aquí es: ¿cómo actualizo mi actividad desde un servicio? . En el siguiente ejemplo, vamos a utilizar dos clases que quizás no conozcas: ResultReceivery IntentService. ResultReceiveres el que nos permitirá actualizar nuestro hilo desde un servicio; IntentServicees una subclase de la Servicecual se genera un hilo para hacer un trabajo de fondo desde allí (debe saber que se Serviceejecuta realmente en el mismo hilo de su aplicación; cuando extiendeService , debe generar manualmente nuevos subprocesos para ejecutar operaciones de bloqueo de la CPU).

El servicio de descarga puede verse así:

public class DownloadService extends IntentService {
    public static final int UPDATE_PROGRESS = 8344;

    public DownloadService() {
        super("DownloadService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {

        String urlToDownload = intent.getStringExtra("url");
        ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
        try {

            //create url and connect
            URL url = new URL(urlToDownload);
            URLConnection connection = url.openConnection();
            connection.connect();

            // this will be useful so that you can show a typical 0-100% progress bar
            int fileLength = connection.getContentLength();

            // download the file
            InputStream input = new BufferedInputStream(connection.getInputStream());

            String path = "/sdcard/BarcodeScanner-debug.apk" ;
            OutputStream output = new FileOutputStream(path);

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                total += count;

                // publishing the progress....
                Bundle resultData = new Bundle();
                resultData.putInt("progress" ,(int) (total * 100 / fileLength));
                receiver.send(UPDATE_PROGRESS, resultData);
                output.write(data, 0, count);
            }

            // close streams 
            output.flush();
            output.close();
            input.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        Bundle resultData = new Bundle();
        resultData.putInt("progress" ,100);

        receiver.send(UPDATE_PROGRESS, resultData);
    }
}

Agregue el servicio a su manifiesto:

<service android:name=".DownloadService"/>

Y la actividad se verá así:

// initialize the progress dialog like in the first example

// this is how you fire the downloader
mProgressDialog.show();
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("url", "url of the file to download");
intent.putExtra("receiver", new DownloadReceiver(new Handler()));
startService(intent);

Aquí es donde ResultReceiverviene a jugar:

private class DownloadReceiver extends ResultReceiver{

    public DownloadReceiver(Handler handler) {
        super(handler);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {

        super.onReceiveResult(resultCode, resultData);

        if (resultCode == DownloadService.UPDATE_PROGRESS) {

            int progress = resultData.getInt("progress"); //get the progress
            dialog.setProgress(progress);

            if (progress == 100) {
                dialog.dismiss();
            }
        }
    }
}

2.1 Usar la biblioteca Groundy

Groundy es una biblioteca que básicamente te ayuda a ejecutar fragmentos de código en un servicio en segundo plano, y se basa en elResultReceiverconcepto que se muestra arriba. Esta biblioteca está en desuso en este momento. Así es como el todo se vería código:

La actividad donde está mostrando el diálogo ...

public class MainActivity extends Activity {

    private ProgressDialog mProgressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim();
                Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build();
                Groundy.create(DownloadExample.this, DownloadTask.class)
                        .receiver(mReceiver)
                        .params(extras)
                        .queue();

                mProgressDialog = new ProgressDialog(MainActivity.this);
                mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                mProgressDialog.setCancelable(false);
                mProgressDialog.show();
            }
        });
    }

    private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
            switch (resultCode) {
                case Groundy.STATUS_PROGRESS:
                    mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS));
                    break;
                case Groundy.STATUS_FINISHED:
                    Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG);
                    mProgressDialog.dismiss();
                    break;
                case Groundy.STATUS_ERROR:
                    Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();
                    mProgressDialog.dismiss();
                    break;
            }
        }
    };
}

Una GroundyTaskimplementación utilizada por Groundy para descargar el archivo y mostrar el progreso:

public class DownloadTask extends GroundyTask {    
    public static final String PARAM_URL = "com.groundy.sample.param.url";

    @Override
    protected boolean doInBackground() {
        try {
            String url = getParameters().getString(PARAM_URL);
            File dest = new File(getContext().getFilesDir(), new File(url).getName());
            DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));
            return true;
        } catch (Exception pokemon) {
            return false;
        }
    }
}

Y solo agregue esto al manifiesto:

<service android:name="com.codeslap.groundy.GroundyService"/>

No podría ser más fácil, creo. Simplemente tome el último frasco de Github y estará listo para comenzar. Manten eso en mente el propósito principal de Groundy es hacer llamadas a aplicaciones REST externas en un servicio en segundo plano y publicar resultados en la interfaz de usuario con facilidad. Si está haciendo algo así en su aplicación, podría ser realmente útil.

2.2 Uso https://github.com/koush/ion

3. Use DownloadManagerclass (GingerBread y más reciente solamente)

GingerBread trajo una nueva característica, DownloadManager que le permite descargar archivos fácilmente y delegar el arduo trabajo de manejar hilos, transmisiones, etc. al sistema.

Primero, veamos un método de utilidad:

/**
 * @param context used to check the device version and DownloadManager information
 * @return true if the download manager is available
 */
public static boolean isDownloadManagerAvailable(Context context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        return true;
    }
    return false;
}

El nombre del método lo explica todo. Una vez que esté seguro de que DownloadManagerestá disponible, puede hacer algo como esto:

String url = "url you want to download";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Some descrition");
request.setTitle("Some title");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    request.allowScanningByMediaScanner();
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext");

// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);

El progreso de la descarga se mostrará en la barra de notificaciones.

Pensamientos finales

El primer y segundo método son solo la punta del iceberg. Hay muchas cosas que debes tener en cuenta si quieres que tu aplicación sea robusta. Aquí hay una breve lista:

  • Debe verificar si el usuario tiene una conexión a Internet disponible
  • Asegúrese de tener los permisos correctos ( INTERNETy WRITE_EXTERNAL_STORAGE); también ACCESS_NETWORK_STATEsi quieres consultar la disponibilidad de internet.
  • Asegúrese de que el directorio donde va a descargar archivos existe y tiene permisos de escritura.
  • Si la descarga es demasiado grande, es posible que desee implementar una forma de reanudar la descarga si fallaron los intentos anteriores.
  • Los usuarios estarán agradecidos si les permite interrumpir la descarga.

A menos que necesite un control detallado del proceso de descarga, considere usar DownloadManager(3) porque ya maneja la mayoría de los elementos enumerados anteriormente.

Pero también considere que sus necesidades pueden cambiar. Por ejemplo, DownloadManager no responde el almacenamiento en caché . Descargará ciegamente el mismo archivo grande varias veces. No hay una manera fácil de arreglarlo después del hecho. Donde si comienzas con un básico HttpURLConnection(1, 2), entonces todo lo que necesitas es agregar un HttpResponseCache. Por lo tanto, el esfuerzo inicial de aprender las herramientas básicas y estándar puede ser una buena inversión.

Esta clase fue desaprobada en el nivel 26 de API. ProgressDialog es un diálogo modal que evita que el usuario interactúe con la aplicación. En lugar de usar esta clase, debe usar un indicador de progreso como ProgressBar, que puede integrarse en la interfaz de usuario de su aplicación. Alternativamente, puede usar una notificación para informar al usuario del progreso de la tarea. Para más detalles Enlace

Cristian
fuente
8
DownloadManager es parte del sistema operativo, lo que significa que siempre estará disponible en GB + y no se puede desinstalar.
Cristian
17
Hay un problema en la respuesta de Cristian. Porque el código en " 1. Use AsyncTask y muestre el progreso de la descarga en un cuadro de diálogo " hace connection.connect (); luego InputStream input = new BufferedInputStream (url.openStream ()); El código hace 2 conexiones al servidor. He logrado cambiar este comportamiento actualizando el código de la siguiente manera InputStream input = new BufferedInputStream (connection.getInputStream ());
nLL
99
Desearía que la documentación de Android fuera tan concisa.
Lou Morda
12
Sugerido a close()las secuencias ( inputy output) en finallylugar de try, de lo contrario, si se produce alguna excepción antes close(), tiene secuencias no cerradas dando vueltas.
Pang
32
No codifique / sdcard/use Environment.getExternalStorageDirectory()en su lugar.
Nima G
106

¡No olvide agregar permisos a su archivo de manifiesto si va a descargar cosas de Internet!

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloandroid"
    android:versionCode="1"
    android:versionName="1.0">

        <uses-sdk android:minSdkVersion="10" />

        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
        <uses-permission android:name="android.permission.INTERNET"></uses-permission>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
        <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>

        <application 
            android:icon="@drawable/icon" 
            android:label="@string/app_name" 
            android:debuggable="true">

        </application>

</manifest>
Pesadilla
fuente
1
Estoy bastante seguro de que no necesita READ_PHONE_STATE y definitivamente no necesita WRITE_EXTERNAL_STORAGE; Es un permiso peligroso que se puede evitar mediante el uso de Storage Access Framework.
Restablece a Monica
32

Si el código anterior funcionará Pero si usted está actualizando su progressbaren onProgressUpdatede Asynctask y pulse el botón Atrás o terminar su actividad AsyncTaskpierde su pista con su interfaz de usuario .Y cuando vuelva a su actividad, incluso si la descarga se está ejecutando en segundo plano verá Sin actualización en la barra de progreso. Entonces, OnResume()intente ejecutar un hilo como runOnUIThreadcon una tarea de temporizador que actualiza su ur progressbarcon valores que se actualizan desde el AsyncTaskfondo en ejecución.

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)
            //while (!Thread.currentThread().isInterrupted()) {
            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);
    }
};

No olvides destruir el hilo cuando tu actividad no sea visible.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}
sheetal
fuente
1
Este es exactamente mi problema. ¿Me puede decir cómo hacer una tarea de temporizador para actualizar la barra de progreso? Cómo hacer que los valores se actualicen desde AsyncTask ejecutándose
user1417127
2
ok, tome una variable global o estática a dónde actualizar sus valores desde asynctask ... o puede insertarla en DataBase para un lado seguro ... para que la aplicación de cierre no obstaculice ... Y cuando reinicie la actividad donde desea actualizar su UI ejecuta el hilo de la interfaz de usuario ... ver el ejemplo a continuación
sheetal
La interfaz de usuario debe tener la nueva referencia ... Como el ProgressBar recién inicializado en mi caso
sheetal
@sheetal, ¡pero funciona bien sin tu código! ¡¿Por qué?! Mi dispositivo es Xperia P con Android 4.0.4. He definido una variable booleana estática que onPreExecute lo establece en verdadero y onPostExecute lo establece en falso. Muestra que estamos descargando o no, por lo que podemos verificar si la variable es igual a verdadera, mostrar el diálogo anterior de la barra de progreso.
Behzad
@sheetal tu código es poco oscuro, ¿puedes darme algún consejo?
iSun
17

Te recomiendo que uses mi Proyecto Netroid , está basado en Volley . Le he agregado algunas características, como la devolución de llamada de eventos múltiples, la gestión de descarga de archivos. Esto podría ser de alguna ayuda.

VinceStyling
fuente
2
¿Es compatible con la descarga de múltiples archivos? Estoy trabajando en un proyecto que permite al usuario descargar programado. En un momento específico (12 AM por ejemplo), el servicio descargará todos los enlaces que el usuario seleccionó antes. Quiero decir que el servicio que necesito debe poder poner en cola los enlaces de descarga y luego descargarlos todos (uno por uno o en paralelo, depende del usuario). Gracias
Hoang Trinh
@piavgh sí, todo lo que quería era satisfacer, puede ver mi apk de muestra, incluía una demostración de gestión de descarga de archivos.
VinceStyling
Gran biblioteca! Funciona bien, aunque tengo problemas para descargar contenido de URL de HTTPS, ya que aunque uno puede configurar su propia SSLSocketFactoryadición, HostnameVerifierno es posible y necesito dicho verificador. Planteó un problema al respecto.
DarkCygnus
enlace para Volley. -> No encontrado
Rumit Patel
14

He modificado la AsyncTaskclase para manejar la creación progressDialogen el mismo contexto. Creo que el siguiente código será más reutilizable. (se puede invocar desde cualquier actividad, solo pasa contexto, archivo de destino, mensaje de diálogo)

public static class DownloadTask extends AsyncTask<String, Integer, String> {
    private ProgressDialog mPDialog;
    private Context mContext;
    private PowerManager.WakeLock mWakeLock;
    private File mTargetFile;
    //Constructor parameters :
    // @context (current Activity)
    // @targetFile (File object to write,it will be overwritten if exist)
    // @dialogMessage (message of the ProgresDialog)
    public DownloadTask(Context context,File targetFile,String dialogMessage) {
        this.mContext = context;
        this.mTargetFile = targetFile;
        mPDialog = new ProgressDialog(context);

        mPDialog.setMessage(dialogMessage);
        mPDialog.setIndeterminate(true);
        mPDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mPDialog.setCancelable(true);
        // reference to instance to use inside listener
        final DownloadTask me = this;
        mPDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                me.cancel(true);
            }
        });
        Log.i("DownloadTask","Constructor done");
    }

    @Override
    protected String doInBackground(String... sUrl) {
        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(sUrl[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();

            // expect HTTP 200 OK, so we don't mistakenly save error report
            // instead of the file
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return "Server returned HTTP " + connection.getResponseCode()
                        + " " + connection.getResponseMessage();
            }
            Log.i("DownloadTask","Response " + connection.getResponseCode());

            // this will be useful to display download percentage
            // might be -1: server did not report the length
            int fileLength = connection.getContentLength();

            // download the file
            input = connection.getInputStream();
            output = new FileOutputStream(mTargetFile,false);

            byte data[] = new byte[4096];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                // allow canceling with back button
                if (isCancelled()) {
                    Log.i("DownloadTask","Cancelled");
                    input.close();
                    return null;
                }
                total += count;
                // publishing the progress....
                if (fileLength > 0) // only if total length is known
                    publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }
        } catch (Exception e) {
            return e.toString();
        } finally {
            try {
                if (output != null)
                    output.close();
                if (input != null)
                    input.close();
            } catch (IOException ignored) {
            }

            if (connection != null)
                connection.disconnect();
        }
        return null;
    }
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // take CPU lock to prevent CPU from going off if the user
        // presses the power button during download
        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                getClass().getName());
        mWakeLock.acquire();

        mPDialog.show();

    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        // if we get here, length is known, now set indeterminate to false
        mPDialog.setIndeterminate(false);
        mPDialog.setMax(100);
        mPDialog.setProgress(progress[0]);

    }

    @Override
    protected void onPostExecute(String result) {
        Log.i("DownloadTask", "Work Done! PostExecute");
        mWakeLock.release();
        mPDialog.dismiss();
        if (result != null)
            Toast.makeText(mContext,"Download error: "+result, Toast.LENGTH_LONG).show();
        else
            Toast.makeText(mContext,"File Downloaded", Toast.LENGTH_SHORT).show();
    }
}
El sirio
fuente
No se olvidó de agregar el permiso de bloqueo Wake <usa-permiso android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu
8

No olvide reemplazar "/ sdcard ..." por un nuevo archivo ("/ mnt / sdcard / ...") de lo contrario obtendrá una FileNotFoundException

ENSATE
fuente
En la parte 2, el cuadro de diálogo Progreso de descarga desde el servicio no puede mostrar el incremento de progreso. devuelve directamente 100, por lo que si 100 verifica directamente setprogress 100 y progresa, ¿cómo incrementar el progreso? Muestra solo 0 progreso pero en realidad descarga en ejecución
Nirav Mehta
muestra solo el 0% de 100 solo otros funcionan correctamente
Nirav Mehta
14
¡No lo hagas! Hay Environment.getExternalStorageDirectory().getAbsolutePath()para obtener un camino a sdcard. Además, no olvide comprobar si el almacenamiento externo está montado - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa
8

Encontré esta publicación de blog muy útil, está usando loopJ para descargar el archivo, solo tiene una función Simple, será útil para algunos chicos nuevos de Android.

chintan adatiya
fuente
7

Mientras comenzaba a aprender el desarrollo de Android, aprendí que ese ProgressDialoges el camino a seguir. Existe el setProgressmétodo ProgressDialogque se puede invocar para actualizar el nivel de progreso a medida que se descarga el archivo.

Lo mejor que he visto en muchas aplicaciones es que personalizan los atributos de este diálogo de progreso para darle una mejor apariencia al diálogo de progreso que la versión estándar. Es bueno mantener al usuario comprometido con alguna animación de rana, elefante o lindos gatos / cachorros. Cualquier animación en el cuadro de diálogo de progreso atrae a los usuarios y no tienen ganas de esperar mucho tiempo.

Sandeep
fuente
5

Mi consejo personal es usar el Diálogo de progreso y construir antes de la ejecución, o iniciar en OnPreExecute(), publicar el progreso a menudo si usa el estilo horizontal de la barra de progreso del diálogo de progreso. La parte restante es optimizar el algoritmo de doInBackground.

Raju tuPepe
fuente
5

Use la biblioteca de Android Query, realmente genial. Puede cambiarla para usarla ProgressDialogcomo ve en otros ejemplos, esta mostrará la vista de progreso de su diseño y la ocultará después de completarla.

File target = new File(new File(Environment.getExternalStorageDirectory(), "ApplicationName"), "tmp.pdf");
new AQuery(this).progress(R.id.progress_view).download(_competition.qualificationScoreCardsPdf(), target, new AjaxCallback<File>() {
    public void callback(String url, File file, AjaxStatus status) {
        if (file != null) {
            // do something with file  
        } 
    }
});
Renetik
fuente
La cuestión es que dejé de usarlo, porque no está mantenido, así que no suponga que esta es una buena respuesta.
Renetik
3

Estoy agregando otra respuesta para otra solución que estoy usando ahora porque Android Query es muy grande y no se mantiene para mantenerse saludable. Así que me mudé a esta https://github.com/amitshekhariitbhu/Fast-Android-Networking .

    AndroidNetworking.download(url,dirPath,fileName).build()
      .setDownloadProgressListener(new DownloadProgressListener() {
        public void onProgress(long bytesDownloaded, long totalBytes) {
            bar.setMax((int) totalBytes);
            bar.setProgress((int) bytesDownloaded);
        }
    }).startDownload(new DownloadListener() {
        public void onDownloadComplete() {
            ...
        }

        public void onError(ANError error) {
            ...
        }
    });
Renetik
fuente
2

Permisos

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission 
   android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Usando HttpURLConnection

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class DownloadFileUseHttpURLConnection extends Activity {

ProgressBar pb;
Dialog dialog;
int downloadedSize = 0;
int totalSize = 0;
TextView cur_val;
String dwnload_file_path =  
"http://coderzheaven.com/sample_folder/sample_file.png";
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button b = (Button) findViewById(R.id.b1);
    b.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
             showProgress(dwnload_file_path);

                new Thread(new Runnable() {
                    public void run() {
                         downloadFile();
                    }
                  }).start();
        }
    });
}

void downloadFile(){

    try {
        URL url = new URL(dwnload_file_path);
        HttpURLConnection urlConnection = (HttpURLConnection)   
 url.openConnection();

        urlConnection.setRequestMethod("GET");
        urlConnection.setDoOutput(true);

        //connect
        urlConnection.connect();

        //set the path where we want to save the file           
        File SDCardRoot = Environment.getExternalStorageDirectory(); 
        //create a new file, to save the downloaded file 
        File file = new File(SDCardRoot,"downloaded_file.png");

        FileOutputStream fileOutput = new FileOutputStream(file);

        //Stream used for reading the data from the internet
        InputStream inputStream = urlConnection.getInputStream();

        //this is the total size of the file which we are downloading
        totalSize = urlConnection.getContentLength();

        runOnUiThread(new Runnable() {
            public void run() {
                pb.setMax(totalSize);
            }               
        });

        //create a buffer...
        byte[] buffer = new byte[1024];
        int bufferLength = 0;

        while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
            fileOutput.write(buffer, 0, bufferLength);
            downloadedSize += bufferLength;
            // update the progressbar //
            runOnUiThread(new Runnable() {
                public void run() {
                    pb.setProgress(downloadedSize);
                    float per = ((float)downloadedSize/totalSize) *     
                    100;
                    cur_val.setText("Downloaded " + downloadedSize +  

                    "KB / " + totalSize + "KB (" + (int)per + "%)" );
                }
            });
        }
        //close the output stream when complete //
        fileOutput.close();
        runOnUiThread(new Runnable() {
            public void run() {
                // pb.dismiss(); // if you want close it..
            }
        });         

    } catch (final MalformedURLException e) {
        showError("Error : MalformedURLException " + e);        
        e.printStackTrace();
    } catch (final IOException e) {
        showError("Error : IOException " + e);          
        e.printStackTrace();
    }
    catch (final Exception e) {
        showError("Error : Please check your internet connection " +  
e);
    }       
}

void showError(final String err){
    runOnUiThread(new Runnable() {
        public void run() {
            Toast.makeText(DownloadFileDemo1.this, err,  
      Toast.LENGTH_LONG).show();
        }
    });
}

void showProgress(String file_path){
    dialog = new Dialog(DownloadFileDemo1.this);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(R.layout.myprogressdialog);
    dialog.setTitle("Download Progress");

    TextView text = (TextView) dialog.findViewById(R.id.tv1);
    text.setText("Downloading file from ... " + file_path);
    cur_val = (TextView) dialog.findViewById(R.id.cur_pg_tv);
    cur_val.setText("Starting download...");
    dialog.show();

     pb = (ProgressBar)dialog.findViewById(R.id.progress_bar);
     pb.setProgress(0);
            pb.setProgressDrawable(
      getResources().getDrawable(R.drawable.green_progress));  
  }
}
vishnuc156
fuente
1

Puede observar el progreso del administrador de descargas utilizando LiveData y corutinas, vea la esencia a continuación

https://gist.github.com/FhdAlotaibi/678eb1f4fa94475daf74ac491874fc0e

data class DownloadItem(val bytesDownloadedSoFar: Long = -1, val totalSizeBytes: Long = -1, val status: Int)

class DownloadProgressLiveData(private val application: Application, private val requestId: Long) : LiveData<DownloadItem>(), CoroutineScope {

    private val downloadManager by lazy {
        application.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
    }

    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

    override fun onActive() {
        super.onActive()
        launch {
            while (isActive) {
                val query = DownloadManager.Query().setFilterById(requestId)
                val cursor = downloadManager.query(query)
                if (cursor.moveToFirst()) {
                    val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
                    Timber.d("Status $status")
                    when (status) {
                        DownloadManager.STATUS_SUCCESSFUL,
                        DownloadManager.STATUS_PENDING,
                        DownloadManager.STATUS_FAILED,
                        DownloadManager.STATUS_PAUSED -> postValue(DownloadItem(status = status))
                        else -> {
                            val bytesDownloadedSoFar = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
                            val totalSizeBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
                            postValue(DownloadItem(bytesDownloadedSoFar.toLong(), totalSizeBytes.toLong(), status))
                        }
                    }
                    if (status == DownloadManager.STATUS_SUCCESSFUL || status == DownloadManager.STATUS_FAILED)
                        cancel()
                } else {
                    postValue(DownloadItem(status = DownloadManager.STATUS_FAILED))
                    cancel()
                }
                cursor.close()
                delay(300)
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        job.cancel()
    }

}
Fahad Alotaibi
fuente
¿Puede proporcionar un ejemplo de caso de uso para esta clase?
Karim Karimov
0

Podemos usar la rutina y el administrador de trabajo para descargar archivos en kotlin.

Agregar una dependencia en build.gradle

    implementation "androidx.work:work-runtime-ktx:2.3.0-beta01"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"

Clase WorkManager

    import android.content.Context
    import android.os.Environment
    import androidx.work.CoroutineWorker
    import androidx.work.WorkerParameters
    import androidx.work.workDataOf
    import com.sa.chat.utils.Const.BASE_URL_IMAGE
    import com.sa.chat.utils.Constants
    import kotlinx.coroutines.delay
    import java.io.BufferedInputStream
    import java.io.File
    import java.io.FileOutputStream
    import java.net.URL

    class DownloadMediaWorkManager(appContext: Context, workerParams: WorkerParameters)
        : CoroutineWorker(appContext, workerParams) {

        companion object {
            const val WORK_TYPE = "WORK_TYPE"
            const val WORK_IN_PROGRESS = "WORK_IN_PROGRESS"
            const val WORK_PROGRESS_VALUE = "WORK_PROGRESS_VALUE"
        }

        override suspend fun doWork(): Result {

            val imageUrl = inputData.getString(Constants.WORK_DATA_MEDIA_URL)
            val imagePath = downloadMediaFromURL(imageUrl)

            return if (!imagePath.isNullOrEmpty()) {
                Result.success(workDataOf(Constants.WORK_DATA_MEDIA_URL to imagePath))
            } else {
                Result.failure()
            }
        }

        private suspend fun downloadMediaFromURL(imageUrl: String?): String? {

            val file = File(
                    getRootFile().path,
                    "IMG_${System.currentTimeMillis()}.jpeg"
            )

            val url = URL(BASE_URL_IMAGE + imageUrl)
            val connection = url.openConnection()
            connection.connect()

            val lengthOfFile = connection.contentLength
            // download the file
            val input = BufferedInputStream(url.openStream(), 8192)
            // Output stream
            val output = FileOutputStream(file)

            val data = ByteArray(1024)
            var total: Long = 0
            var last = 0

            while (true) {

                val count = input.read(data)
                if (count == -1) break
                total += count.toLong()

                val progress = (total * 100 / lengthOfFile).toInt()

                if (progress % 10 == 0) {
                    if (last != progress) {
                        setProgress(workDataOf(WORK_TYPE to WORK_IN_PROGRESS,
                                WORK_PROGRESS_VALUE to progress))
                    }
                    last = progress
                    delay(50)
                }
                output.write(data, 0, count)
            }

            output.flush()
            output.close()
            input.close()

            return file.path

        }

        private fun getRootFile(): File {

            val rootDir = File(Environment.getExternalStorageDirectory().absolutePath + "/AppName")

            if (!rootDir.exists()) {
                rootDir.mkdir()
            }

            val dir = File("$rootDir/${Constants.IMAGE_FOLDER}/")

            if (!dir.exists()) {
                dir.mkdir()
            }
            return File(dir.absolutePath)
        }
    }

Comience a descargar a través del administrador de trabajo en la clase de actividad

 private fun downloadImage(imagePath: String?, id: String) {

            val data = workDataOf(WORK_DATA_MEDIA_URL to imagePath)
            val downloadImageWorkManager = OneTimeWorkRequestBuilder<DownloadMediaWorkManager>()
                    .setInputData(data)
                    .addTag(id)
                    .build()

            WorkManager.getInstance(this).enqueue(downloadImageWorkManager)

            WorkManager.getInstance(this).getWorkInfoByIdLiveData(downloadImageWorkManager.id)
                    .observe(this, Observer { workInfo ->

                        if (workInfo != null) {
                            when {
                                workInfo.state == WorkInfo.State.SUCCEEDED -> {
                                    progressBar?.visibility = View.GONE
                                    ivDownload?.visibility = View.GONE
                                }
                                workInfo.state == WorkInfo.State.FAILED || workInfo.state == WorkInfo.State.CANCELLED || workInfo.state == WorkInfo.State.BLOCKED -> {
                                    progressBar?.visibility = View.GONE
                                    ivDownload?.visibility = View.VISIBLE
                                }
                                else -> {
                                    if(workInfo.progress.getString(WORK_TYPE) == WORK_IN_PROGRESS){
                                        val progress = workInfo.progress.getInt(WORK_PROGRESS_VALUE, 0)
                                        progressBar?.visibility = View.VISIBLE
                                        progressBar?.progress = progress
                                        ivDownload?.visibility = View.GONE

                                    }
                                }
                            }
                        }
                    })

        }
Kinjal
fuente