Android Retrofit 파일 다운로드 진행 상황 표시 문제 해결 방법
Retrofit 2.0 에서 상세 하 게 이 글 을 사용 하여 retrofit 의 용법 을 상세 하 게 소개 하 였 다.또한 retrofit 에서 우 리 는 Response Body 를 통 해 파일 을 다운로드 할 수 있 습 니 다.그러나 retrofit 에 서 는 다운로드 진 도 를 표시 하 는 인 터 페 이 스 를 제공 하지 않 았 다.프로젝트 에서 만약 에 사용자 가 파일 을 다운로드 하면 실시 간 으로 사용자 에 게 다운로드 진 도 를 표시 할 수 없 기 때문에 사용자 의 체험 도 매우 나쁘다.그럼 retrofit 에서 파일 다운로드 에 사용 되 는 다운로드 진 도 를 실시 간 으로 추적 하 는 방법 을 소개 합 니 다.
시범 을 보이다
Retrofit 파일 다운로드 진도 업데이트 실현
retrofit 2.0 에서 그 는 Okhttp 에 의존 하기 때문에 우리 가 이 문 제 를 해결 하려 면 이 OKhttp 에서 시작 해 야 한다.Okhttp 에 의존 패키지 Okio 가 있 습 니 다.Okio 도 square 회사 가 개발 한 것 으로 자바.io 와 자바.nio 의 보충 으로 데 이 터 를 쉽게 접근 하고 저장 하 며 처리 할 수 있 습 니 다.여기 서 는 Okio 의 Source 류 를 사용 해 야 합 니 다.여기 서 Source 는 InputStream 이 라 고 볼 수 있 습 니 다.오 키 오의 자세 한 사용 은 여기 서 소개 하지 않 겠 습 니 다.구체 적 인 실현 을 살 펴 보 자.
여기 서 우 리 는 먼저 인 터 페 이 스 를 써 서 다운로드 의 진 도 를 감청 하 는 데 사용한다.파일 의 다운로드 에 대해 우 리 는 다운로드 의 진도,파일 의 총 크기,그리고 작업 이 완료 되 었 는 지 알 아야 한다.그래서 아래 의 인터페이스 가 생 겼 다.
package com.ljd.retrofit.progress;
/**
* Created by ljd on 3/29/16.
*/
public interface ProgressListener {
/**
* @param progress
* @param total
* @param done
*/
void onProgress(long progress, long total, boolean done);
}
파일 다운로드 에 대해 서 는 Response Body 류 의 일부 방법 을 다시 써 야 합 니 다.
package com.ljd.retrofit.progress;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
/**
* Created by ljd on 3/29/16.
*/
public class ProgressResponseBody extends ResponseBody {
private final ResponseBody responseBody;
private final ProgressListener progressListener;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
this.responseBody = responseBody;
this.progressListener = progressListener;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
progressListener.onProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
return bytesRead;
}
};
}
}
위의 ProgressResponse Body 클래스 에서 읽 은 파일 의 바이트 수 를 계산 하고 ProgressListener 인 터 페 이 스 를 호출 했 습 니 다.그래서 이 ProgressListener 인 터 페 이 스 는 하위 스 레 드 에서 실 행 됩 니 다.이 Progress Response Body 를 어떻게 사용 하 는 지 살 펴 보 겠 습 니 다.
package com.ljd.retrofit.progress;
import android.util.Log;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
/**
* Created by ljd on 4/12/16.
*/
public class ProgressHelper {
private static ProgressBean progressBean = new ProgressBean();
private static ProgressHandler mProgressHandler;
public static OkHttpClient.Builder addProgress(OkHttpClient.Builder builder){
if (builder == null){
builder = new OkHttpClient.Builder();
}
final ProgressListener progressListener = new ProgressListener() {
//
@Override
public void onProgress(long progress, long total, boolean done) {
Log.d("progress:",String.format("%d%% done
",(100 * progress) / total));
if (mProgressHandler == null){
return;
}
progressBean.setBytesRead(progress);
progressBean.setContentLength(total);
progressBean.setDone(done);
mProgressHandler.sendMessage(progressBean);
}
};
// , ResponseBody,
builder.networkInterceptors().add(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder().body(
new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
});
return builder;
}
public static void setProgressHandler(ProgressHandler progressHandler){
mProgressHandler = progressHandler;
}
}
우 리 는 OkhttpClient 에 차단 기 를 추가 해서 사용자 정의 ProgressResponse Body 를 사용 합 니 다.그리고 여기 서 우 리 는 ProgressListener 인 터 페 이 스 를 실현 할 수 있다.다운로드 진 도 를 가 져 왔 습 니 다.그러나 여기 에는 여전히 문제 가 존재 한다.방금 이 ProgressListener 인 터 페 이 스 는 하위 스 레 드 에서 실행 된다 고 말 했다.즉,ProgressListener 라 는 인터페이스 에서 ui 작업 을 할 수 없습니다.파일 을 다운로드 하 는 진 도 는 ui 디 스 플레이 를 위 한 진도 항목 이 필요 합 니 다.분명히 이것 은 우리 가 원 하 는 결과 가 아니다.이 럴 때 우 리 는 Handler 를 사용 해 야 한다.우 리 는 Handler 를 통 해 하위 스 레 드 의 ProgressListener 데 이 터 를 ui 스 레 드 에 보 내 처리 할 수 있 습 니 다.즉,우리 가 ProgressListener 인터페이스 에서 의 조작 은 그 인 자 를 Handler 를 통 해 보 내 는 것 일 뿐이다.위의 코드 에서 우 리 는 ProgressHandler 를 통 해 메 시 지 를 보 낸 것 이 분명 하 다.그럼 구체 적 인 조작 을 살 펴 보 겠 습 니 다.
ProgressListener 의 인 자 를 저장 할 대상 을 만 듭 니 다.
package com.example.ljd.retrofit.pojo;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ljd on 3/29/16.
*/
public class RetrofitBean {
private Integer total_count;
private Boolean incompleteResults;
private List<Item> items = new ArrayList<Item>();
/**
*
* @return
* The totalCount
*/
public Integer getTotalCount() {
return total_count;
}
/**
*
* @param totalCount
* The total_count
*/
public void setTotalCount(Integer totalCount) {
this.total_count = totalCount;
}
/**
*
* @return
* The incompleteResults
*/
public Boolean getIncompleteResults() {
return incompleteResults;
}
/**
*
* @param incompleteResults
* The incomplete_results
*/
public void setIncompleteResults(Boolean incompleteResults) {
this.incompleteResults = incompleteResults;
}
/**
*
* @return
* The items
*/
public List<Item> getItems() {
return items;
}
}
그리고 Progress Handler 클래스 를 만 들 고 있 습 니 다.
package com.ljd.retrofit.progress;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
/**
* Created by ljd on 4/12/16.
*/
public abstract class ProgressHandler {
protected abstract void sendMessage(ProgressBean progressBean);
protected abstract void handleMessage(Message message);
protected abstract void onProgress(long progress, long total, boolean done);
protected static class ResponseHandler extends Handler{
private ProgressHandler mProgressHandler;
public ResponseHandler(ProgressHandler mProgressHandler, Looper looper) {
super(looper);
this.mProgressHandler = mProgressHandler;
}
@Override
public void handleMessage(Message msg) {
mProgressHandler.handleMessage(msg);
}
}
}
위의 Progress Handler 는 추상 적 인 부류 이다.여기 서 우 리 는 Handler 대상 을 통 해 메 시 지 를 보 내 고 처리 해 야 한다.그래서 두 가지 추상 적 인 방법 인 sendmessage 와 handleMessage 를 정의 했다.그 다음 에 다운로드 진 도 를 표시 하 는 추상 적 인 방법 인 onProgress 를 정 의 했 습 니 다.이 onProgress 는 ui 스 레 드 에서 호출 해 야 합 니 다.마지막 으로 Handler 를 계승 하 는 ResponseHandler 내부 클래스 를 만 들 었 습 니 다.메모리 유출 을 피하 기 위해 static 키 워드 를 사용 합 니 다.다음은 DownloadProgressHandler 클래스 를 만 듭 니 다.그 는 ProgressHandler 에 계승 하여 메 시 지 를 보 내 고 처리 합 니 다.
package com.ljd.retrofit.progress;
import android.os.Looper;
import android.os.Message;
/**
* Created by ljd on 4/12/16.
*/
public abstract class DownloadProgressHandler extends ProgressHandler{
private static final int DOWNLOAD_PROGRESS = 1;
protected ResponseHandler mHandler = new ResponseHandler(this, Looper.getMainLooper());
@Override
protected void sendMessage(ProgressBean progressBean) {
mHandler.obtainMessage(DOWNLOAD_PROGRESS,progressBean).sendToTarget();
}
@Override
protected void handleMessage(Message message){
switch (message.what){
case DOWNLOAD_PROGRESS:
ProgressBean progressBean = (ProgressBean)message.obj;
onProgress(progressBean.getBytesRead(),progressBean.getContentLength(),progressBean.isDone());
}
}
}
여기에서 우 리 는 메 시 지 를 받 은 후에 추상 적 인 방법 인 onProgress 를 호출 합 니 다.그러면 우 리 는 DownloadProgressHandler 대상 을 만 들 고 onProgress 를 실현 하면 됩 니 다.위의 분석 에 대해 서 는 어떻게 사용 하 는 지 살 펴 보 겠 습 니 다.
package com.example.ljd.retrofit.download;
import android.app.ProgressDialog;
import android.os.Environment;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.example.ljd.retrofit.R;
import com.ljd.retrofit.progress.DownloadProgressHandler;
import com.ljd.retrofit.progress.ProgressHelper;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import butterknife.ButterKnife;
import butterknife.OnClick;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class DownloadActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
ButterKnife.bind(this);
}
@Override
protected void onDestroy() {
ButterKnife.unbind(this);
super.onDestroy();
}
@OnClick(R.id.start_download_btn)
public void onClickButton(){
retrofitDownload();
}
private void retrofitDownload(){
//
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setProgressNumberFormat("%1d KB/%2d KB");
dialog.setTitle(" ");
dialog.setMessage(" , ...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setCancelable(false);
dialog.show();
Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://msoftdl.360.cn");
OkHttpClient.Builder builder = ProgressHelper.addProgress(null);
DownloadApi retrofit = retrofitBuilder
.client(builder.build())
.build().create(DownloadApi.class);
ProgressHelper.setProgressHandler(new DownloadProgressHandler() {
@Override
protected void onProgress(long bytesRead, long contentLength, boolean done) {
Log.e(" ", String.valueOf(Looper.getMainLooper() == Looper.myLooper()));
Log.e("onProgress",String.format("%d%% done
",(100 * bytesRead) / contentLength));
Log.e("done","--->" + String.valueOf(done));
dialog.setMax((int) (contentLength/1024));
dialog.setProgress((int) (bytesRead/1024));
if(done){
dialog.dismiss();
}
}
});
Call<ResponseBody> call = retrofit.retrofitDownload();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
InputStream is = response.body().byteStream();
File file = new File(Environment.getExternalStorageDirectory(), "12345.apk");
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
fos.flush();
}
fos.close();
bis.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
}
총결산위의 실현 에 대해 우 리 는 OkhttpClient 를 통 해 이 루어 진 것 을 알 수 있다.바로 retrofit 2.0 에서 OkHttp 에 의존 하기 때문에 OkHttp 의 기능 retrofit 에 도 모두 갖 추고 있다.이 기능 을 이용 하여,우 리 는 맞 춤 형 OkhttpClient 를 통 해 우리 의 retrofit 를 설정 할 수 있 습 니 다.
원본 다운로드:https://github.com/lijiangdong/retrofit-example
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【Android】Retrofit + Rx리트로핏의 Rx화에 관해서는 제이크 신작 을 사용한 것 같다.농담하다 현재 본가의 리트로핏에는 어댑터가 있는 것 같은데, 제이크 신작DEPRECATED이 있다. 사용 환경 구조 app/build.gradle에 다음을...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.