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 AsyncTask
e 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 AsyncTask
aparê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 onProgressUpdate
e onPreExecute
executado 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: ResultReceiver
e IntentService
. ResultReceiver
é o que nos permitirá atualizar nosso encadeamento a partir de um serviço; IntentService
é uma subclasse da Service
qual gera um encadeamento para Service
executar 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 ResultReceiver
vem 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 noResultReceiver
conceito 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 GroundyTask
implementaçã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.
3. Use DownloadManager
classe ( GingerBread
e apenas mais recentes)
O GingerBread trouxe um novo recurso, DownloadManager
que 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 DownloadManager
está 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 (
INTERNET
e WRITE_EXTERNAL_STORAGE
); também ACCESS_NETWORK_STATE
se 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