Android 使用 Volley 下載文件附進度條
Tags: android • volley • tutorial | (4 min read)
"Volley is not suitable for large download or streaming operations, since Volley holds all responses in memory during parsing. For large download operations, consider using an alternative like DownloadManager."
這是真的,特別是內存小的手機,使用Volley下載很多文件或者單個大文件就不是很恰當了。這裡的large是相對手機內存大小而定。
首先準備一個帶Progress Bar的layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/download"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:background="@drawable/rounded_edge"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintCircleRadius="5dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:background="@drawable/ic_download"
android:progressDrawable="@drawable/circular_progress_bar" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="@string/download_desc"
android:textColor="#fff" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:orientation="horizontal">
<Button
android:id="@+id/dialog_button_agree"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:drawableStart="@drawable/ic_agree"
android:text="@string/agree"
android:textAlignment="viewStart"
android:textColor="#F4E829" />
<View
android:layout_width="1px"
android:layout_height="match_parent"
android:layout_marginHorizontal="16dp"
android:background="@android:color/darker_gray" />
<Button
android:id="@+id/dialog_button_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:drawableStart="@drawable/ic_disagress"
android:text="@string/back"
android:textAlignment="viewStart"
android:textColor="#F4E829" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
準備一下進度條。
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="270"
android:toDegrees="270">
<shape
android:innerRadiusRatio="2.44"
android:shape="ring"
android:thickness="2dp"
android:useLevel="true">
<gradient
android:angle="0"
android:endColor="#FFF000"
android:startColor="#FFF000"
android:type="sweep"
android:useLevel="false" />
</shape>
</rotate>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="80dp"
android:viewportWidth="80"
android:viewportHeight="80">
<path
android:fillColor="#686868"
android:pathData="M40,7.5163C48.6699,7.5163 56.8366,10.902 62.9673,17.0327C69.098,23.1634 72.4837,31.3301 72.4837,40C72.4837,48.6699 69.098,56.8366 62.9673,62.9673C56.8366,69.098 48.6699,72.4837 40,72.4837C31.3301,72.4837 23.1634,69.098 17.0327,62.9673C10.902,56.8366 7.5163,48.6699 7.5163,40C7.5163,31.3301 10.902,23.1634 17.0327,17.0327C23.1634,10.902 31.3301,7.5163 40,7.5163ZM40,5C20.6699,5 5,20.6699 5,40C5,59.3301 20.6699,75 40,75C59.3301,75 75,59.3301 75,40C75,20.6699 59.3301,5 40,5Z" />
<path
android:fillColor="#FFF000"
android:pathData="M41.0378,49.9393L56.2402,34.4966L54.0244,32.3541L41.5146,45.0422V20H38.4293V45.0422L25.9195,32.3541L23.7037,34.4966L38.9061,49.9393H17V53H63V49.9393H41.0378ZM40.0281,46.6003H39.9719H40.0281Z" />
</vector>
封裝一下 Volley。
package com.szeching.volleyexample;
import androidx.annotation.Nullable;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import java.util.Map;
public class DownloadRequest extends Request<byte[]> {
private final Response.Listener<byte[]> mListener;
public Map<String, String> responseHeaders;
public DownloadRequest(int method, String url, Response.Listener<byte[]> listener, @Nullable Response.ErrorListener errorListener) {
super(method, url, errorListener);
setShouldCache(false);
this.mListener = listener;
}
@Override
protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
responseHeaders = response.headers;
return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
}
@Override
protected void deliverResponse(byte[] response) {
mListener.onResponse(response);
}
}
在Activity中發起請求並設置進度條進度。
package com.szeching.volleyexample;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.widget.ProgressBar;
import androidx.appcompat.app.AppCompatActivity;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.Volley;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download);
}
private void requestSource() {
request = new DownloadRequest(Request.Method.GET, "https://example.com/example.mp4", new Response.Listener<byte[]>() {
@Override
public void onResponse(byte[] response) {
HashMap<String, Object> map = new HashMap<String, Object>();
try {
if (response != null) {
long file_length = response.length;
InputStream input = new ByteArrayInputStream(response);
File path = getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_MOVIES);
File file = new File(path, "filename.mp4");
map.put("resume_path", file.toString());
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));
byte[] data = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
int progress = (int) ((int) total * 100 / file_length);
Log.d("onProgress",""+progress);
progressBar.setProgress(progress);
}
output.flush();
output.close();
input.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HurlStack());
mRequestQueue.add(request);
}
}