Android Retrofit下载文件进度

时间:2020-02-23 14:29:13  来源:igfitidea点击:

在本教程中,我们将创建一个Android应用程序,该应用程序使用Retrofit从URL下载文件。
要了解翻新的基础知识,请访问本教程。

Android改造下载文件

我们可以通过以下方式创建改造调用以下载文件:

@GET
Call<ResponseBody> downloadFileWithc(@Url String urlString);

我们可以传递要下载的文件的URL。
如果我们要下载资源中存在的文件,则可以执行以下操作:

@GET("/resource/path_to_file_with_extension")
Call<ResponseBody> downloadFileStatic();

建议在@GET顶部使用@Streaming批注来下载文件。
否则,翻新会将整个文件移到内存中。
使用@Streaming可以在不占用内存的情况下当前访问字节。

当使用@ Streaming时,必须将写入下载数据的代码添加到单独的线程中。

使用enqueue方法,我们可以启动请求。
其中,我们需要创建一个AsyncTask或者使用RxJava。
在本教程中,我们将使用前者。

在下面将要构建的android应用程序中,我们将在ProgressBar上显示文件下载进度。

项目设置

将以下依赖项添加到您应用的build.gradle中:

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.okhttp3:okhttps:3.10.0'

在列表中添加以下权限:

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

以下是我们的项目结构的外观:

代码

下面给出了" activity_main.xml"布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <TextView
      android:id="@+id/txtProgressPercent"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Downloaded 0%"
      android:textColor="@color/colorAccent"
      android:textStyle="bold"
      android:textAppearance="@style/Base.TextAppearance.AppCompat.Display1"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" 

  <ProgressBar
      android:id="@+id/progressBar"
      style="?android:attr/progressBarStyleHorizontal"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginLeft="16dp"
      android:layout_marginRight="16dp"
      android:layout_marginTop="16dp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/txtProgressPercent" 

  <Button
      android:id="@+id/button"
      style="@style/ButtonStyle"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="16dp"
      android:text="DOWNLOAD FILE"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/progressBar" 

</android.support.constraint.ConstraintLayout>

我们在styles.xml中的按钮上设置了样式。

下面给出了RetrofitInterface.java类的代码:

package com.theitroad.androidretrofitdownloadfileprogress;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Streaming;
import retrofit2.http.Url;

public interface RetrofitInterface {

  @Streaming
  @GET
  Call<ResponseBody> downloadFileByUrl(@Url String fileUrl);

}

MainActivity.java类的代码如下:

package com.theitroad.androidretrofitdownloadfileprogress;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

public class MainActivity extends AppCompatActivity {

  TextView txtProgressPercent;
  ProgressBar progressBar;
  Button btnDownloadFile;

  DownloadZipFileTask downloadZipFileTask;
  private static final String TAG = "MainActivity";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      askForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, 101);

      txtProgressPercent = findViewById(R.id.txtProgressPercent);
      progressBar = findViewById(R.id.progressBar);
      btnDownloadFile = findViewById(R.id.button);

      btnDownloadFile.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              downloadZipFile();
          }
      });
  }

  private void downloadZipFile() {

      RetrofitInterface downloadService = createService(RetrofitInterface.class, "https://github.com/");
      Call<ResponseBody> call = downloadService.downloadFileByUrl("anupamchugh/AnimateTextAndImageView/archive/master.zip");

      call.enqueue(new Callback<ResponseBody>() {
          @Override
          public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
              if (response.isSuccessful()) {
                  Log.d(TAG, "Got the body for the file");

                  Toast.makeText(getApplicationContext(), "Downloading...", Toast.LENGTH_SHORT).show();

                  downloadZipFileTask = new DownloadZipFileTask();
                  downloadZipFileTask.execute(response.body());

              } else {
                  Log.d(TAG, "Connection failed " + response.errorBody());
              }
          }

          @Override
          public void onFailure(Call<ResponseBody> call, Throwable t) {
              t.printStackTrace();
              Log.e(TAG, t.getMessage());
          }
      });
  }

  public <T> T createService(Class<T> serviceClass, String baseUrl) {
      Retrofit retrofit = new Retrofit.Builder()
              .baseUrl(baseUrl)
              .client(new OkHttpClient.Builder().build())
              .build();
      return retrofit.create(serviceClass);
  }

  private class DownloadZipFileTask extends AsyncTask<ResponseBody, Pair<Integer, Long>, String> {

      @Override
      protected void onPreExecute() {
          super.onPreExecute();

      }

      @Override
      protected String doInBackground(ResponseBody... urls) {
          //Copy you logic to calculate progress and call
          saveToDisk(urls[0], "theitroad-project.zip");
          return null;
      }

      protected void onProgressUpdate(Pair<Integer, Long>... progress) {

          Log.d("API123", progress[0].second + " ");

          if (progress[0].first == 100)
              Toast.makeText(getApplicationContext(), "File downloaded successfully", Toast.LENGTH_SHORT).show();

          if (progress[0].second > 0) {
              int currentProgress = (int) ((double) progress[0].first/(double) progress[0].second * 100);
              progressBar.setProgress(currentProgress);

              txtProgressPercent.setText("Progress " + currentProgress + "%");

          }

          if (progress[0].first == -1) {
              Toast.makeText(getApplicationContext(), "Download failed", Toast.LENGTH_SHORT).show();
          }

      }

      public void doProgress(Pair<Integer, Long> progressDetails) {
          publishProgress(progressDetails);
      }

      @Override
      protected void onPostExecute(String result) {

      }
  }

  private void saveToDisk(ResponseBody body, String filename) {
      try {

          File destinationFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);

          InputStream inputStream = null;
          OutputStream outputStream = null;

          try {

              inputStream = body.byteStream();
              outputStream = new FileOutputStream(destinationFile);
              byte data[] = new byte[4096];
              int count;
              int progress = 0;
              long fileSize = body.contentLength();
              Log.d(TAG, "File Size=" + fileSize);
              while ((count = inputStream.read(data)) != -1) {
                  outputStream.write(data, 0, count);
                  progress += count;
                  Pair<Integer, Long> pairs = new Pair<>(progress, fileSize);
                  downloadZipFileTask.doProgress(pairs);
                  Log.d(TAG, "Progress: " + progress + "/" + fileSize + " >>>> " + (float) progress/fileSize);
              }

              outputStream.flush();

              Log.d(TAG, destinationFile.getParent());
              Pair<Integer, Long> pairs = new Pair<>(100, 100L);
              downloadZipFileTask.doProgress(pairs);
              return;
          } catch (IOException e) {
              e.printStackTrace();
              Pair<Integer, Long> pairs = new Pair<>(-1, Long.valueOf(-1));
              downloadZipFileTask.doProgress(pairs);
              Log.d(TAG, "Failed to save the file!");
              return;
          } finally {
              if (inputStream != null) inputStream.close();
              if (outputStream != null) outputStream.close();
          }
      } catch (IOException e) {
          e.printStackTrace();
          Log.d(TAG, "Failed to save the file!");
          return;
      }
  }

  private void askForPermission(String permission, Integer requestCode) {
      if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) {

          if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission)) {
              ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);

          } else {
              ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);
          }
      } else if (ContextCompat.checkSelfPermission(MainActivity.this, permission) == PackageManager.PERMISSION_DENIED) {
          Toast.makeText(getApplicationContext(), "Permission was denied", Toast.LENGTH_SHORT).show();
      }
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) {

          if (requestCode == 101)
              Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show();
      } else {
          Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
      }
  }
}

在上面的代码中,我们正在下载GitHub存储库zip文件。

我们在上面的代码中进行以下操作:

  • 运行时权限–我们需要此权限才能将文件保存在手机存储中。

  • 使用OkHttp构建改造服务

  • 使用response.body()从Async中的url下载文件。

  • 在AsyncTask内部,我们创建一个公共方法doProgress,其中调用AsyncTask方法publishProgress()。

  • publishProgress从doInBackground触发AsyncTask的ʻonProgressUpdate()方法。

  • 这样我们就可以确定文件下载的进度,并在ProgressBar上对其进行更新。

  • 下载的文件路径在内部存储器的downloads文件夹内设置。