In this tutorial, we’ll create an android application which downloads a file from the URL using Retrofit.
To know the basics of Retrofit, visit this tutorial.
在本教程中,我们将创建一个Android应用程序,该应用程序使用Retrofit从URL下载文件。
要了解翻新的基础知识,请访问本教程 。
Android改造下载文件 (Android Retrofit Download File)
We can create a retrofit call in the following way in order to download file:
我们可以通过以下方式创建改造调用以下载文件:
@GET
Call<ResponseBody> downloadFileWithc(@Url String urlString);
We can pass the URL of the file we want to download. If we are downloading a file present in the resources we can do this:
我们可以传递要下载的文件的URL。 如果我们要下载资源中存在的文件,则可以执行以下操作:
@GET("/resource/path_to_file_with_extension")
Call<ResponseBody> downloadFileStatic();
It’s recommended to use a @Streaming
annotation on top of the @GET for downloading files. Otherwise Retrofit would move the entire file into memory. Using @Streaming the bytes would be accessed currently without eating up the memory.
建议在@Streaming
顶部使用@Streaming
批注来下载文件。 否则,翻新会将整个文件移到内存中。 使用@Streaming可以在不占用内存的情况下当前访问字节。
When using @Streaming
you must add the code that writes the downloaded data, into a separate thread.
使用@Streaming
,必须将写入下载数据的代码添加到单独的线程中。
Using the enqueue method we can start the request.
Inside it, we need to create an AsyncTask or use RxJava. We’ll go with the former in this tutorial.
使用enqueue方法,我们可以启动请求。
在其中,我们需要创建一个AsyncTask或使用RxJava。 在本教程中,我们将使用前者。
In the following android application that we are going to build, we’ll show the file download progress on a ProgressBar.
在下面将要构建的android应用程序中,我们将在ProgressBar上显示文件下载进度。
项目设置 (Project Setup)
Add the following dependencies to your app’s build.gradle:
将以下依赖项添加到应用程序的build.gradle中:
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.okhttp3:okhttps:3.10.0'
Add the following permissions in your Manifest:
在清单中添加以下权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
Following is how our Project Structure looks:
以下是我们的项目结构的外观:
码 (Code)
The code for the activity_main.xml
layout is given below:
下面给出了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>
We’ve set a style on the button in the styles.xml.
我们在styles.xml中的按钮上设置了样式。
The code for the RetrofitInterface.java class is given below:
下面给出了RetrofitInterface.java类的代码:
package com.journaldev.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);
}
The code for the MainActivity.java class is given below:
MainActivity.java类的代码如下:
package com.journaldev.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], "journaldev-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();
}
}
}
In the above code, we are downloading a GitHub repository zip file.
在上面的代码中,我们正在下载GitHub存储库zip文件。
We do the following list of things in the above code:
我们在上面的代码中执行以下操作:
- Runtime Permissions – We need this for saving the file in our phone storage. 运行时权限 –我们需要此权限才能将文件保存在手机存储中。
- Building a Retrofit Service using OkHttp 使用OkHttp构建改造服务
- Downloading the file from the url in the Async by using
response.body()
. 使用response.body()
从Async中的url下载文件。 - Inside the AsyncTask, we create a public method
doProgress
in which we invoke the AsyncTask method publishProgress(). 在AsyncTask内,我们创建一个公共方法doProgress
在其中调用AsyncTask方法publishProgress()。 - publishProgress triggers the
onProgressUpdate()
method of AsyncTask from the doInBackground. publishProgress从doInBackground触发AsyncTask的onProgressUpdate()
方法。 - Doing so we can determine the progress of the file download and update it on the ProgressBar. 这样我们就可以确定文件下载的进度,并在ProgressBar上对其进行更新。
- The downloaded file path is set inside the downloads folder in the Internal Storage. 下载的文件路径在内部存储器的downloads文件夹内设置。
The output of the application in action is given below:
实际应用程序的输出如下:
And a screenshot from our File manager proves that the file is downloaded:
来自我们的文件管理器的屏幕快照证明文件已下载:
This brings an end to this tutorial. You can download the project from the link below:
本教程到此结束。 您可以从下面的链接下载项目:
翻译自: https://www.journaldev.com/22475/android-retrofit-download-file-progress