Pobierz plik na Androida i pokazuj postęp w ProgressDialog

1046

Próbuję napisać prostą aplikację, która zostanie zaktualizowana. W tym celu potrzebuję prostej funkcji, która może pobrać plik i pokazać aktualny postęp w ProgressDialog. Wiem, jak to zrobić ProgressDialog, ale nie jestem pewien, jak wyświetlić bieżący postęp i jak pobrać plik.

Tom Leese
źródło
2
Mam nadzieję, że poniższy link może ci pomóc ... androidhive.info/2012/04/
Ganesh Katikar
1
Aby uzyskać szybką poprawkę, zapoznaj się z tą odpowiedzią
Richa
stackoverflow.com/a/43069239/3879214 sprawdź tę odpowiedź
Anu Martin

Odpowiedzi:

1873

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

AsyncTaskBę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 onProgressUpdatei onPreExecutedział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ć: ResultReceiveri IntentService. ResultReceiverto ten, który pozwoli nam zaktualizować nasz wątek z usługi; IntentServicejest podklasą, z Servicektórej odradza się wątek do wykonywania zadań w tle (powinieneś wiedzieć, że Servicedziała tak naprawdę w tym samym wątku Twojej aplikacji; po rozszerzeniu Servicemusisz 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 ResultReceivernadchodzi 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ższejResultReceiverkoncepcji. 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;
            }
        }
    };
}

GroundyTaskRealizacja 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.

2.2 Użyj https://github.com/koush/ion

3. Użyj DownloadManagerklasy ( GingerBreadi tylko nowsze)

GingerBread wprowadził nową funkcję, DownloadManagerktó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 DownloadManagerjest 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 ( INTERNETi WRITE_EXTERNAL_STORAGE); także ACCESS_NETWORK_STATEjeś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

Cristian
źródło
8
DownloadManager jest częścią systemu operacyjnego, co oznacza, że ​​zawsze będzie dostępny w GB + i nie będzie można go odinstalować.
Cristian
17
W odpowiedzi Cristiana jest problem. Ponieważ kod w „ 1. Użyj AsyncTask i pokaż postęp pobierania w oknie dialogowym ” robi connection.connect (); następnie InputStream input = new BufferedInputStream (url.openStream ()); kod wykonuje 2 połączenia z serwerem. Udało mi się zmienić to zachowanie, aktualizując kod w następujący sposób InputStream input = new BufferedInputStream (connection.getInputStream ());
nLL
99
Chciałbym, żeby dokumentacja Androida była tak zwięzła.
Lou Morda
12
Sugerowane do close()strumieni ( inputi output) finallyzamiast try, w przeciwnym razie, jeśli wcześniej close()zostanie zgłoszony wyjątek , wiszą niezamknięte strumienie.
Pang
32
Zamiast tego nie sdcard/używaj kodu / użycia Environment.getExternalStorageDirectory().
Nima G
106

Nie zapomnij dodać uprawnień do pliku manifestu, jeśli będziesz pobierać rzeczy z Internetu!

<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>
Koszmar
źródło
1
Jestem pewien, że nie potrzebujesz READ_PHONE_STATE i zdecydowanie nie potrzebujesz WRITE_EXTERNAL_STORAGE; jest to niebezpieczne uprawnienie, którego można uniknąć, korzystając z Storage Access Framework.
Przywróć Monikę
32

Tak powyższy kod będzie działać .Ale jeśli aktualizowania progressbarin onProgressUpdatez Asynctask i naciśnięciu przycisku Wstecz lub zakończyć swoją działalność AsyncTasktraci swój utwór z UI .i kiedy wrócisz do swojej działalności, nawet jeśli pobieranie jest uruchomiony w tle widać brak aktualizacji na pasku postępu. Więc OnResume()spróbuj uruchomić wątek jak runOnUIThreadw przypadku zadania licznika czasu, które aktualizuje twoje progressbarwartości aktualizując je z AsyncTaskdziałającego tła.

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

Nie zapomnij zniszczyć wątku, gdy Twoja aktywność nie jest widoczna.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}
arkuszowy
źródło
1
To jest dokładnie mój problem. Czy możesz mi powiedzieć, jak wykonać zadanie licznika czasu, aby zaktualizować pasek postępu? Jak uzyskać aktualizację wartości z uruchomionego
AsyncTask
2
okk, weź zmienną globalną lub statyczną, aby zaktualizować wartości z asynctask ... lub możesz wstawić ją do bazy danych dla bezpiecznej strony .. tak, aby zamknięcie aplikacji nie przeszkodziło .. A kiedy ponownie uruchomisz działanie, w którym chcesz zaktualizować ur Interfejs użytkownika uruchamia wątek interfejsu użytkownika. Patrz przykład poniżej
arkusz
Interfejs użytkownika powinien mieć świeże odniesienie. Podobnie jak w moim przypadku nowo zainicjowany pasek postępu
arkusz
@ sheheal, ale działa dobrze bez twojego kodu! Dlaczego?! Moje urządzenie to Xperia P z systemem Android 4.0.4. Zdefiniowałem statyczną zmienną boolowską, w której onPreExecute ustawia ją na true, a onPostExecute ustawia ją na false. Pokazuje, że pobieramy, czy nie, więc możemy sprawdzić, czy zmienna jest równa true, pokazać poprzednie okno dialogowe paska postępu.
Behzad
@ sheheal twój kod jest trochę niejasny, czy możesz mi powiedzieć jakąś radę?
iSun
17

Polecam korzystanie z mojego projektu Netroid , jest oparty na Volley . Dodałem do niego pewne funkcje, takie jak wywołanie zwrotne wielu zdarzeń, zarządzanie pobieraniem plików. To może być pomocne.

VinceStyling
źródło
2
Czy obsługuje pobieranie wielu plików? Pracuję nad projektem, który pozwala użytkownikowi zaplanować pobieranie. W określonym czasie (na przykład 12 rano) usługa pobierze wszystkie łącza wybrane wcześniej przez użytkownika. Mam na myśli, że usługa, której potrzebuję, musi mieć możliwość umieszczenia w kolejce linków do pobrania, a następnie pobrania ich wszystkich (pojedynczo lub równolegle, zależnie od użytkownika). Dziękuję
Hoang Trinh
@piavgh tak, wszystko, czego chciałeś, to spełnić, możesz pobrać moją próbkę apk, zawierała demo zarządzania pobieraniem plików.
VinceStyling
Świetna biblioteka! Działa dobrze, chociaż mam problemy z pobieraniem treści z adresu URL HTTPS, ponieważ chociaż można ustawić własne SSLSocketFactorydodawanie, HostnameVerifiernie jest możliwe i potrzebuję takiego weryfikatora. Podniósł problem dotyczący tego.
DarkCygnus
link do Volley. -> Nie znaleziono
Rumit Patel
14

Zmodyfikowałem AsyncTaskklasę, aby obsługiwać tworzenie progressDialogw tym samym kontekście. Myślę, że następujący kod będzie bardziej przydatny. (można go wywołać z dowolnego działania, wystarczy przekazać kontekst, plik docelowy, komunikat w oknie dialogowym)

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();
    }
}
Syryjczyk
źródło
Nie zapomnij dodać uprawnienia Wake lock <uses-permission android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu
8

Nie zapomnij zastąpić „/ sdcard ...” nowym plikiem („/ mnt / sdcard / ...”), w przeciwnym razie otrzymasz wyjątek FileNotFoundException

ENSATE
źródło
W części 2 Pobieranie z usługi Okno dialogowe Postęp nie może wyświetlać przyrostu postępu. zwraca bezpośrednio 100, więc jeśli 100, to bezpośrednio sprawdza setprogress 100 i postęp, jak zwiększyć postęp ?? wyświetla tylko 0 postęp, ale faktycznie trwa pobieranie
Nirav Mehta
wyświetla tylko 0% na 100 tylko inne działa poprawnie
Nirav Mehta
14
Nie rób tego! Istnieje sposób Environment.getExternalStorageDirectory().getAbsolutePath()uzyskania ścieżki do sdcard. Nie zapomnij również sprawdzić, czy pamięć zewnętrzna jest zamontowana - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa
8

Uważam, że ten post na blogu jest bardzo pomocny. Korzysta z loopJ do pobierania pliku, ma tylko jedną prostą funkcję, będzie pomocny dla niektórych nowych facetów z Androidem.

Chintan Adatiya
źródło
7

Kiedy zacząłem uczyć się programowania na Androida, nauczyłem się, że ProgressDialogto jest właściwa droga. Istnieje setProgressmetoda, ProgressDialogktórą można wywołać, aby zaktualizować poziom postępu podczas pobierania pliku.

Najlepsze, jakie widziałem w wielu aplikacjach, to to, że dostosowują atrybuty tego okna dialogowego postępu, aby uzyskać lepszy wygląd i działanie okna dialogowego postępu niż wersja standardowa. Dobrze jest utrzymać użytkownika zaangażowanego w animację żaby, słonia lub uroczych kotów / szczeniąt. Każda animacja w oknie dialogowym postępu przyciąga użytkowników i nie mają oni ochoty długo czekać.

Sandeep
źródło
Uszkodzony link -> Pokaż pasek postępu podczas pobierania pliku w
Androidzie
5

Moja osobista rada to korzystanie z Dialogu Postępów i budowanie go przed wykonaniem lub inicjowanie w OnPreExecute(), publikowanie postępów często, jeśli używasz poziomego stylu paska postępu w oknie dialogowym postępu. Pozostała część to optymalizacja algorytmu doInBackground.

Raju yourPepe
źródło
5

Skorzystaj z biblioteki zapytań na Androida, naprawdę bardzo fajnej. Możesz ją zmienić tak, aby używała, ProgressDialogjak widać na innych przykładach, ta pokaże widok postępu z twojego układu i ukryje go po zakończeniu.

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
źródło
Chodzi o to, że przestałem go używać, ponieważ jest on nieobsługiwany, więc nie przypuszczaj, że to już dobra odpowiedź.
Renetik
3

Dodaję kolejną odpowiedź na inne rozwiązanie, którego używam teraz, ponieważ Android Query jest tak duży i nie wymaga konserwacji, aby zachować zdrowie. Więc przeniosłem się do tego 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
źródło
2

Uprawnienia

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

Korzystanie z 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
źródło
1

Postępy menedżera pobierania można obserwować za pomocą LiveData i coroutines, patrz streszczenie poniżej

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
źródło
Czy możesz podać przykładowy przykład dla tej klasy?
Karim Karimov
0

Do pobierania plików w Kotlin możemy użyć menedżera koroutin i pracy.

Dodaj zależność w build.gradle

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

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

Rozpocznij pobieranie za pośrednictwem menedżera pracy w klasie aktywności

 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
źródło