Es gibt viele Möglichkeiten, Dateien herunterzuladen. Im Folgenden werde ich die häufigsten Wege veröffentlichen. Es liegt an Ihnen, zu entscheiden, welche Methode für Ihre App besser ist.
1. Verwenden Sie AsyncTask
den Download-Fortschritt und zeigen Sie ihn in einem Dialogfeld an
Mit dieser Methode können Sie einige Hintergrundprozesse ausführen und gleichzeitig die Benutzeroberfläche aktualisieren (in diesem Fall aktualisieren wir einen Fortschrittsbalken).
Importe:
import android.os.PowerManager;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
Dies ist ein Beispielcode:
// 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
}
});
Das AsyncTask
wird so aussehen:
// 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;
}
Die Methode oben ( doInBackground
) wird immer in einem Hintergrundthread ausgeführt. Sie sollten dort keine UI-Aufgaben ausführen. Auf der anderen Seite wird das onProgressUpdate
und onPreExecute
auf dem UI-Thread ausgeführt, sodass Sie dort den Fortschrittsbalken ändern können:
@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();
}
Damit dies ausgeführt werden kann, benötigen Sie die Berechtigung WAKE_LOCK.
<uses-permission android:name="android.permission.WAKE_LOCK" />
2. Vom Service herunterladen
Die große Frage hier ist: Wie aktualisiere ich meine Aktivität von einem Dienst aus? . Im nächsten Beispiel werden wir zwei Klassen verwenden, die Ihnen möglicherweise nicht bekannt sind: ResultReceiver
und IntentService
. ResultReceiver
ist derjenige, der es uns ermöglicht, unseren Thread von einem Dienst aus zu aktualisieren; IntentService
ist eine Unterklasse, Service
die einen Thread erzeugt, um von dort aus Hintergrundarbeiten auszuführen (Sie sollten wissen, dass ein Service
Thread tatsächlich im selben Thread Ihrer App ausgeführt wird; wenn Sie ihn erweitern Service
, müssen Sie manuell neue Threads erzeugen, um CPU-Blockierungsvorgänge auszuführen).
Der Download-Service kann folgendermaßen aussehen:
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);
}
}
Fügen Sie den Service Ihrem Manifest hinzu:
<service android:name=".DownloadService"/>
Und die Aktivität wird so aussehen:
// 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);
Hier ist wo ResultReceiver
kommt zu spielen:
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 Verwenden Sie die Groundy-Bibliothek
Groundy ist eine Bibliothek, mit der Sie im Wesentlichen Codeteile in einem Hintergrunddienst ausführen können. Sie basiert auf demResultReceiver
oben gezeigten Konzept. Diese Bibliothek istderzeit veraltet . Sowürdeder gesamte Code aussehen:
Die Aktivität, in der Sie den Dialog anzeigen ...
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;
}
}
};
}
Eine GroundyTask
Implementierung, mit der Groundy die Datei herunterlädt und den Fortschritt anzeigt :
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;
}
}
}
Und fügen Sie dies einfach dem Manifest hinzu:
<service android:name="com.codeslap.groundy.GroundyService"/>
Einfacher geht es nicht, denke ich. Nehmen Sie einfach das neueste Glas von Github und schon kann es losgehen. Beachten Sie, dass der Hauptzweck von Groundy darin besteht, externe REST-APIs in einem Hintergrunddienst aufzurufen und die Ergebnisse problemlos an die Benutzeroberfläche zu senden. Wenn Sie so etwas in Ihrer App tun, kann dies sehr nützlich sein.
3. Verwenden Sie DownloadManager
class ( GingerBread
und nur neuere)
GingerBread brachte eine neue Funktion mit, DownloadManager
mit der Sie Dateien einfach herunterladen und die harte Arbeit des Umgangs mit Threads, Streams usw. an das System delegieren können.
Schauen wir uns zunächst eine Dienstprogrammmethode an:
/**
* @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;
}
Der Name der Methode erklärt alles. Sobald Sie sicher sind, dass DownloadManager
es verfügbar ist, können Sie Folgendes tun:
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);
Der Download-Fortschritt wird in der Benachrichtigungsleiste angezeigt.
Abschließende Gedanken
Erste und zweite Methode sind nur die Spitze des Eisbergs. Es gibt viele Dinge, die Sie beachten müssen, wenn Ihre App robust sein soll. Hier ist eine kurze Liste:
- Sie müssen überprüfen, ob der Benutzer über eine Internetverbindung verfügt
- Stellen Sie sicher, dass Sie über die richtigen Berechtigungen (
INTERNET
und WRITE_EXTERNAL_STORAGE
) verfügen . auch ACCESS_NETWORK_STATE
wenn Sie die Internetverfügbarkeit überprüfen möchten.
- Stellen Sie sicher, dass das Verzeichnis, in das Sie Dateien herunterladen möchten, vorhanden ist und über Schreibberechtigungen verfügt.
- Wenn der Download zu groß ist, möchten Sie möglicherweise eine Möglichkeit implementieren, den Download fortzusetzen, wenn frühere Versuche fehlgeschlagen sind.
- Benutzer sind dankbar, wenn Sie ihnen erlauben, den Download zu unterbrechen.
Wenn Sie keine detaillierte Kontrolle über den Download-Prozess benötigen, sollten Sie DownloadManager
(3) verwenden, da die meisten der oben aufgeführten Elemente bereits verarbeitet werden.
Bedenken Sie aber auch, dass sich Ihre Bedürfnisse ändern können. Zum Beispiel DownloadManager
wird keine Antwort zwischengespeichert . Es wird dieselbe große Datei mehrmals blind heruntergeladen. Es gibt keine einfache Möglichkeit, das Problem nachträglich zu beheben. Wenn Sie mit einem Basic HttpURLConnection
(1, 2) beginnen, müssen Sie lediglich ein hinzufügen HttpResponseCache
. Die anfängliche Anstrengung, die grundlegenden Standardwerkzeuge zu erlernen, kann daher eine gute Investition sein.
Diese Klasse wurde in API-Stufe 26 nicht mehr unterstützt. ProgressDialog ist ein modaler Dialog, der verhindert, dass der Benutzer mit der App interagiert. Anstatt diese Klasse zu verwenden, sollten Sie eine Fortschrittsanzeige wie ProgressBar verwenden, die in die Benutzeroberfläche Ihrer App eingebettet werden kann. Alternativ können Sie eine Benachrichtigung verwenden, um den Benutzer über den Fortschritt der Aufgabe zu informieren. Für weitere Details Link