Android Retrofit下载文件进度
在本教程中,我们将创建一个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文件夹内设置。