Istnieje wiele sposobów pobierania plików. Poniżej zamieszczę najczęstsze sposoby; od Ciebie zależy, która metoda będzie lepsza dla Twojej aplikacji.
1. Użyj AsyncTask
i pokaż postęp pobierania w oknie dialogowym
Ta metoda pozwoli Ci wykonać niektóre procesy w tle i jednocześnie zaktualizować interfejs użytkownika (w tym przypadku zaktualizujemy pasek postępu).
Import:
import android.os.PowerManager;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
To jest przykładowy kod:
// 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
}
});
AsyncTask
Będzie wyglądać następująco:
// 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;
}
Metoda powyżej ( doInBackground
) działa zawsze w wątku w tle. Nie powinieneś tam wykonywać żadnych zadań interfejsu użytkownika. Z drugiej strony onProgressUpdate
i onPreExecute
działają w wątku interfejsu użytkownika, dzięki czemu można zmienić pasek postępu:
@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();
}
Aby to uruchomić, potrzebujesz uprawnienia WAKE_LOCK.
<uses-permission android:name="android.permission.WAKE_LOCK" />
2. Pobierz z usługi
Najważniejsze pytanie brzmi: jak zaktualizować swoją aktywność w serwisie? . W następnym przykładzie użyjemy dwóch klas, o których możesz nie wiedzieć: ResultReceiver
i IntentService
. ResultReceiver
to ten, który pozwoli nam zaktualizować nasz wątek z usługi; IntentService
jest podklasą, z Service
której odradza się wątek do wykonywania zadań w tle (powinieneś wiedzieć, że Service
działa tak naprawdę w tym samym wątku Twojej aplikacji; po rozszerzeniu Service
musisz ręcznie spawnować nowe wątki, aby uruchomić operacje blokowania procesora).
Usługa pobierania może wyglądać następująco:
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);
}
}
Dodaj usługę do swojego manifestu:
<service android:name=".DownloadService"/>
Aktywność będzie wyglądać następująco:
// 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);
Oto ResultReceiver
nadchodzi gra:
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 Użyj biblioteki Groundy
Groundy to biblioteka, która zasadniczo pomaga uruchamiać fragmenty kodu w usłudze w tle i jest oparta na powyższejResultReceiver
koncepcji. Ta biblioteka jest obecnie nieaktualna . Tak wyglądałby cały kod:
Aktywność, w której wyświetlasz okno dialogowe ...
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;
}
}
};
}
GroundyTask
Realizacja wykorzystywane przez Groundy aby pobrać plik i pokazać postęp:
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;
}
}
}
I po prostu dodaj to do manifestu:
<service android:name="com.codeslap.groundy.GroundyService"/>
Myślę, że nie może być łatwiej. Wystarczy pobrać najnowszy słoik od Github i jesteś gotowy do pracy. Należy pamiętać, że głównym celem Groundy jest nawiązywanie połączeń z zewnętrznymi aplikacjami REST api w usłudze działającej w tle i łatwe publikowanie wyników w interfejsie użytkownika. Jeśli robisz coś takiego w swojej aplikacji, może to być bardzo przydatne.
3. Użyj DownloadManager
klasy ( GingerBread
i tylko nowsze)
GingerBread wprowadził nową funkcję, DownloadManager
która umożliwia łatwe pobieranie plików i delegowanie ciężkiej pracy związanej z obsługą wątków, strumieni itp. Do systemu.
Najpierw zobaczmy metodę narzędzia:
/**
* @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;
}
Nazwa metody wyjaśnia wszystko. Gdy masz pewność, że DownloadManager
jest dostępny, możesz zrobić coś takiego:
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);
Postęp pobierania będzie widoczny na pasku powiadomień.
Końcowe przemyślenia
Pierwsza i druga metoda to tylko wierzchołek góry lodowej. Jest wiele rzeczy, o których musisz pamiętać, jeśli chcesz, aby Twoja aplikacja była niezawodna. Oto krótka lista:
- Musisz sprawdzić, czy użytkownik ma dostępne połączenie internetowe
- Upewnij się, że masz odpowiednie uprawnienia (
INTERNET
i WRITE_EXTERNAL_STORAGE
); także ACCESS_NETWORK_STATE
jeśli chcesz sprawdzić dostępność Internetu.
- Upewnij się, że katalog, w którym chcesz pobrać pliki, istnieje i ma uprawnienia do zapisu.
- Jeśli pobieranie jest zbyt duże, możesz zaimplementować sposób wznowienia pobierania, jeśli poprzednie próby się nie powiodły.
- Użytkownicy będą wdzięczni, jeśli pozwolisz im przerwać pobieranie.
O ile nie potrzebujesz szczegółowej kontroli procesu pobierania, rozważ użycie DownloadManager
(3), ponieważ obsługuje on już większość wyżej wymienionych elementów.
Pamiętaj jednak, że Twoje potrzeby mogą się zmienić. Na przykład DownloadManager
nie buforuje odpowiedzi . Ślepo pobierze ten sam duży plik wiele razy. Nie ma łatwy sposób, aby naprawić to po fakcie. Jeśli zaczniesz od podstawowego HttpURLConnection
(1, 2), wystarczy dodać HttpResponseCache
. Tak więc początkowy wysiłek nauki podstawowych, standardowych narzędzi może być dobrą inwestycją.
Ta klasa została wycofana z poziomu 26 interfejsu API. ProgressDialog to modalne okno dialogowe, które uniemożliwia użytkownikowi interakcję z aplikacją. Zamiast korzystać z tej klasy, należy użyć wskaźnika postępu, takiego jak ProgressBar, który można osadzić w interfejsie aplikacji. Alternatywnie możesz użyć powiadomienia, aby poinformować użytkownika o postępie zadania. Więcej informacji Link