Baixe um arquivo com Android e mostre o progresso em um ProgressDialog

1046

Estou tentando escrever um aplicativo simples que é atualizado. Para isso, preciso de uma função simples que possa baixar um arquivo e mostrar o progresso atual em um arquivo ProgressDialog. Eu sei como fazer isso ProgressDialog, mas não tenho certeza de como exibir o progresso atual e como baixar o arquivo em primeiro lugar.

Tom Leese
fonte
2
Espero que o link abaixo possa ajudá-lo ... androidhive.info/2012/04/…
Ganesh Katikar
1
Para solução rápida enviamos esta resposta
Richa
stackoverflow.com/a/43069239/3879214 verifique esta resposta
Anu Martin

Respostas:

1873

Existem várias maneiras de baixar arquivos. A seguir, publicarei as formas mais comuns; cabe a você decidir qual método é melhor para seu aplicativo.

1. Use AsyncTaske mostre o progresso do download em uma caixa de diálogo

Este método permitirá que você execute alguns processos em segundo plano e atualize a interface do usuário ao mesmo tempo (nesse caso, atualizaremos uma barra de progresso).

Importações:

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

Este é um código de exemplo:

// 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
    }
});

A AsyncTaskaparência será assim:

// 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;
    }

O método acima ( doInBackground) é executado sempre em um encadeamento em segundo plano. Você não deve executar nenhuma tarefa de interface do usuário lá. Por outro lado, o onProgressUpdatee onPreExecuteexecutado no segmento interface do usuário, por isso não é possível alterar a barra de progresso:

    @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 isso seja executado, você precisa da permissão WAKE_LOCK.

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

2. Baixe do Serviço

A grande questão aqui é: como atualizo minha atividade de um serviço? . No próximo exemplo, vamos usar duas classes que você talvez não saiba: ResultReceivere IntentService. ResultReceiveré o que nos permitirá atualizar nosso encadeamento a partir de um serviço; IntentServiceé uma subclasse da Servicequal gera um encadeamento para Serviceexecutar o trabalho em segundo plano a partir daí (você deve saber que um é executado no mesmo encadeamento do seu aplicativo; quando você se estende Service, deve gerar manualmente novos encadeamentos para executar operações de bloqueio de CPU).

O serviço de download pode ficar assim:

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);
    }
}

Adicione o serviço ao seu manifesto:

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

E a atividade ficará assim:

// 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);

Aqui está onde ResultReceivervem para jogar:

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 biblioteca Groundy

Groundy é uma biblioteca que basicamente ajuda a executar trechos de código em um serviço em segundo plano e é baseada noResultReceiverconceito mostrado acima. Esta biblioteca está obsoleta no momento. É assim que ocódigo inteiro ficaria:

A atividade em que você está mostrando a caixa de 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;
            }
        }
    };
}

Uma GroundyTaskimplementação usada pelo Groundy para baixar o arquivo e mostrar o progresso:

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;
        }
    }
}

E basta adicionar isso ao manifesto:

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

Não poderia ser mais fácil, eu acho. Basta pegar o pote mais recente do Github e você está pronto para começar. Lembre-se de que o principal objetivo do Groundy é fazer chamadas para APIs REST externas em um serviço em segundo plano e postar resultados na interface do usuário com facilidade. Se você estiver fazendo algo assim no seu aplicativo, pode ser realmente útil.

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

3. Use DownloadManagerclasse ( GingerBreade apenas mais recentes)

O GingerBread trouxe um novo recurso, DownloadManagerque permite baixar arquivos facilmente e delegar o trabalho árduo de lidar com threads, fluxos, etc. no sistema.

Primeiro, vamos ver um método utilitário:

/**
 * @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;
}

O nome do método explica tudo. Quando tiver certeza de que DownloadManagerestá disponível, você pode fazer algo assim:

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);

O progresso do download será exibido na barra de notificação.

Pensamentos finais

Primeiro e segundo métodos são apenas a ponta do iceberg. Há muitas coisas que você deve ter em mente se quiser que seu aplicativo seja robusto. Aqui está uma breve lista:

  • Você deve verificar se o usuário tem uma conexão com a Internet disponível
  • Verifique se você possui as permissões corretas ( INTERNETe WRITE_EXTERNAL_STORAGE); também ACCESS_NETWORK_STATEse você quiser verificar a disponibilidade da Internet.
  • Certifique-se de que o diretório em que você deseja baixar os arquivos exista e tenha permissões de gravação.
  • Se o download for muito grande, você poderá implementar uma maneira de continuar o download, se as tentativas anteriores falharem.
  • Os usuários ficarão agradecidos se você permitir que eles interrompam o download.

A menos que você precise de controle detalhado do processo de download, considere usar DownloadManager(3) porque ele já lida com a maioria dos itens listados acima.

Mas considere também que suas necessidades podem mudar. Por exemplo, DownloadManager não há cache de resposta . Ele fará o download cego do mesmo arquivo grande várias vezes. Não há uma maneira fácil de corrigi-lo após o fato. Onde se você começar com um básico HttpURLConnection(1, 2), tudo o que precisa é adicionar um HttpResponseCache. Portanto, o esforço inicial de aprender as ferramentas básicas e padrão pode ser um bom investimento.

Esta classe foi descontinuada no nível 26 da API. ProgressDialog é uma caixa de diálogo modal, que impede o usuário de interagir com o aplicativo. Em vez de usar essa classe, você deve usar um indicador de progresso como ProgressBar, que pode ser incorporado na interface do usuário do seu aplicativo. Como alternativa, você pode usar uma notificação para informar o usuário sobre o progresso da tarefa. Para mais detalhes Link

Cristian
fonte
8
O DownloadManager faz parte do sistema operacional, o que significa que sempre estará disponível em GB + e não pode ser desinstalado.
Cristian
17
Há um problema na resposta de Cristian. Como o código em " 1. Use AsyncTask e mostre o progresso do download em uma caixa de diálogo ", connection.connect (); então InputStream input = new BufferedInputStream (url.openStream ()); O código faz 2 conexões com o servidor. Eu consegui alterar esse comportamento atualizando o código da seguinte forma InputStream input = new BufferedInputStream (connection.getInputStream ());
Nll 16/11/12
99
Eu gostaria que a documentação do Android fosse concisa.
Lou Morda
12
Sugerido para close()os fluxos ( inpute output) em finallyvez de try, caso contrário, se alguma exceção for lançada antes close(), você terá fluxos não fechados por aí.
Pang
32
Não codifique / sdcard/use em Environment.getExternalStorageDirectory()vez disso.
Nima G
106

Não se esqueça de adicionar permissões ao seu arquivo de manifesto se você estiver baixando coisas da 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>
Mnightmare
fonte
1
Tenho certeza de que você não precisa de READ_PHONE_STATE e definitivamente não precisa de WRITE_EXTERNAL_STORAGE; é uma permissão perigosa que pode ser evitada usando o Storage Access Framework.
Reintegrar Monica
32

Sim, o código acima irá trabalhar .Mas se você está atualizando seu progressbarem onProgressUpdatede Asynctask e você pressionar o botão de volta ou terminar sua atividade AsyncTaskperde a sua pista com o seu UI .E quando você voltar para a sua actividade, mesmo que o download estiver em execução no fundo você vai ver nenhuma atualização na barra de progresso. Portanto, OnResume()tente executar um encadeamento como runOnUIThreadem uma tarefa de timer que atualiza seu progressbarvalor com a atualização do AsyncTaskfundo em execução.

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);
    }
};

Não se esqueça de Destruir o encadeamento quando sua atividade não estiver visível.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}
sheetal
fonte
1
Este é exatamente o meu problema. Você pode me dizer como executar uma tarefa de timer para atualizar a barra de progresso? Como obter valores de atualização do AsyncTask correr atrás
user1417127
2
okk leve uma variável global ou estática para onde atualizar seus valores de asynctask ... ou você pode inseri-lo no DataBase para o lado seguro .. para que o aplicativo de fechamento não atrapalhe .. e quando você reiniciar a atividade em que deseja atualizar seu UI executar o exemplo UI thread..see abaixo
sheetal
A interface do usuário deve ter o reference..Like fresco o recém initalized ProgressBar no meu caso
sheetal
@ planilha, mas funciona bem sem o seu código! Por quê?! Meu dispositivo é o Xperia P com Android 4.0.4. Eu defini uma variável booleana estática que onPreExecute a define como verdadeira e onPostExecute a define como false. Isso mostra que estamos fazendo o download ou não, para que possamos verificar se a variável é igual a true, mostre o diálogo anterior da barra de progresso.
Behzad
@sheetal seu código é um pouco obscuro, você pode me dizer alguns conselhos?
ISun #
17

Eu recomendo que você use o meu Projeto Netroid , baseado no Volley . Eu adicionei alguns recursos, como retorno de chamada em vários eventos, gerenciamento de download de arquivos. Isso pode ser de alguma ajuda.

VinceStyling
fonte
2
Ele suporta o download de vários arquivos? Estou trabalhando em um projeto que permite ao usuário baixar agendado. Em um horário específico (00:00, por exemplo), o serviço fará o download de todo o link que o usuário selecionou anteriormente. Quero dizer, o serviço necessário precisa ser capaz de enfileirar links de download e depois baixar todos eles (um por um ou paralelo, depende do usuário). Obrigado
Hoang Trinh
@piavgh sim, tudo o que você queria era satisfazer, você pode conferir meu apk de amostra, que incluía uma demonstração de gerenciamento de download de arquivos.
VinceStyling
Ótima biblioteca! Funciona bem, embora eu esteja tendo problemas para baixar conteúdo de URLs HTTPS, como se alguém pudesse definir seu próprio SSLSocketFactoryadicionando um HostnameVerifiernão é possível e eu preciso desse verificador. Levantou um problema sobre isso.
DarkCygnus 23/05
link para Volley. -> Não encontrado
Rumit Patel
14

Eu modifiquei a AsyncTaskclasse para lidar com a criação de progressDialogno mesmo contexto. Acho que o código a seguir será mais reutilizável. (pode ser chamado a partir de qualquer atividade, basta passar o contexto, o arquivo de destino, a mensagem 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();
    }
}
O sírio
fonte
Não se esqueceu de adicionar permissão de bloqueio Wake <usos-permissão Android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu
8

Não se esqueça de substituir "/ sdcard ..." por novo arquivo ("/ mnt / sdcard / ..."), caso contrário, você receberá uma FileNotFoundException

ENSATE
fonte
Na parte 2 A caixa de diálogo Download do serviço Progresso não pode exibir o incremento do progresso. -lo retorno direto 100, por isso, se 100 ele verificação setProgress direta 100 e progredir mais, como ao progresso incremento ??-lo exibir apenas 0 progresso, mas realmente baixar correndo
Nirav Mehta
-lo exibir apenas 0% dos 100 único outro trabalho adequadamente
Nirav Mehta
14
Não faça isso! Existe Environment.getExternalStorageDirectory().getAbsolutePath()para obter um caminho para o sdcard. Também não se esqueça de verificar se o armazenamento externo é montado - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
NAXA
8

Achei este post de blog muito útil, está usando loopJ para baixar arquivo, ele tem apenas uma função Simples, será útil para alguns novos caras do Android.

chintan adatiya
fonte
7

Enquanto eu começava a aprender o desenvolvimento do Android, eu aprendi que esse ProgressDialogé o caminho a percorrer. Existe um setProgressmétodo ProgressDialogque pode ser chamado para atualizar o nível de progresso à medida que o arquivo é baixado.

O melhor que eu já vi em muitos aplicativos é que eles personalizam os atributos dessa caixa de diálogo de progresso para dar uma aparência melhor à caixa de diálogo de progresso do que a versão padrão. É bom manter o usuário envolvido com algumas animações como sapo, elefante ou gatos / cachorros fofos. Qualquer animação na caixa de diálogo de progresso atrai os usuários e eles não sentem vontade de ficar esperando por muito tempo.

Sandeep
fonte
5

Meu conselho pessoal é usar a caixa de diálogo Progresso e criar antes da execução, ou iniciar em OnPreExecute(), publicar o progresso com freqüência, se você usar a barra de estilo horizontal de progresso da caixa de diálogo de progresso. A parte restante é otimizar o algoritmo de doInBackground.

Raju yourPepe
fonte
5

Use a biblioteca de consultas do Android, muito legal mesmo. Você pode alterá-lo para usar ProgressDialogcomo você vê em outros exemplos, este mostrará a exibição de progresso do seu layout e ocultará após a conclusão.

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
fonte
O problema é que parei de usá-lo, porque não é mantido, então não suponha que essa seja mais uma boa resposta.
Renetik
3

Estou adicionando outra resposta para outra solução que estou usando agora porque o Android Query é muito grande e sem manutenção para manter a saúde. Então, mudei para este 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
fonte
2

Permissões

  <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
fonte
1

Você pode observar o progresso do gerenciador de downloads usando LiveData e coroutines, veja a lista abaixo

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
fonte
Você pode fornecer um exemplo de caso de uso para esta classe?
Karim Karimov
0

Podemos usar a rotina e o gerenciador de trabalho para baixar arquivos no kotlin.

Adicione uma dependência no build.gradle

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

Classe 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)
        }
    }

Iniciar o download através do gerente de trabalho na classe de atividade

 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
fonte