1、使用jar包
由于retrofit2与1的写法有很大的变化,并且对okhttp存在着依赖。所以需要确定选择使用方向,以免陷入不必要的坑中。
本篇讲采用retrofit2 + okhttp3做结合使用。在导包过程中本人也遇到了一些不兼容的麻烦,再此做记录。
retrofit-2.1.0.jar、okhttp-3.4.1.jar、okio-1.6.0.jar、converter-gson-2.1.0.jar
另外我们服务端使用springmvc框架,通过@responseBody返回。前期引入gson包为gson-2.4.jar总报错不知何故,最后换成gson-2.7.jar就可以了。
2、使用retrofit
retrofit使用是有一定规律性,我们可以将其归纳后提出,封装一个针对retrofit的制造者。
public class RetrofitBuilder{
private static Retrofit retrofit;
public synchronized static Retrofit buildRetrofit(String url){
if(retrofit == null){
OkHttpClient client = new OkHttpClient
.Builder()
.retryOnConnectionFailure(true)
.build();
retrofit = new Retrofit
.Builder
.client(client)
.baseUrl(url)
.addConverterFactory((GsonConverterFactory.create()))
.build();
}
return retrofit;
}
}
retrofit对应请求接口
public interface FileuploadService{
/**
*@param parts
*@param map(请求参数)
*@return(JsonRetrun实体类)
*/
@Multipart
@POST("uploader")
Call<JsonReturn> uploadFilesWithParts(@Part() List<MultipartBody.Part> parts , @QueryMap Map<String , String> map);
/**
*@param body
*@param map(请求参数)
*@return(JsonRetrun实体类)
*/
@Multipart
@POST("uploader")
Call<JsonReturn> uploadFilesWithBody(@Body() MultipartBody body , @QueryMap Map<String , String> map);
}
回执泛型实体类
public class JsonReturn{
private String errcode;
private String errmsg;
private String msg;
private Object object;
set/get方法.....
}
接口中Multipart参数生成,可以封装一个创造者。
public class MultipartBuiler{
public static MultipartBody filesToBody(List<File> files , RetrofitCallback<JsonReturn> callback){
for(File file : files){
RequestBody requestBody = RequestBody.create(MediaType.parse("img/png"),file);
FileRequestBody body = new FileRequestBody(reqeustBody , callback , file);
builder.addFormDataPart("file" , file.getName , body);
}
builder.setType(MultipartBody.FORM);
MultipartBody multipartBody = builder.build();
return multipartBody;
}
public staitc List<MultipartBody.Part> filesToParts(List<File> files , RetrofitCallback<JsonReturn> callback){
List<MultipartBody.Part> parts = new ArrayList<>(files.size());
for(File file : files){
RequestBody requestBody = RequestBody.create(MediaType.parse("img/png"),file);
FileRequestBody body = new FileRequestBody(reqeustBody , callback , file);
MultipartBody.Part part = MultipartBody.Part.createFormData("file" , file.getName() , body);
parts.add(part);
}
return parts;
}
}
以上可以实现文件的上传了。
3、有的时候我们在项目中需要监听文件的上传进度,那要如何实现呢。继续往下看思路。
上传的过程,我们可以考虑为request的交互过程,所以我们完全可以针对okhttp中的requestBody做些扩展。
/**
* 扩展Okhttp的请求体, 实现上传时的进度监听
* @author xuximing
*
* @param <T>
*/
public class FileRequestBody<T> extends RequestBody {
/**
* 实体请求体
*/
private RequestBody requestBody;
/**
* 上传回调接口
*/
private RetrofitCallback<T> callback;
/**
* 文件
*/
private File file ;
/**
* 包装完成的BufferedSink
*/
private BufferedSink bufferedSink;
public FileRequestBody(RequestBody requestBody , RetrofitCallback<T> callback , File file){
super();
this.requestBody = requestBody;
this.callback = callback;
this.file = file;
}
@Override
public long contentLength() throws IOException {
return requestBody.contentLength();
}
@Override
public MediaType contentType() {
return requestBody.contentType();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
if(bufferedSink == null){
//包装
bufferedSink = Okio.buffer(sink(sink));
}
//写入
requestBody.writeTo(bufferedSink);
//必须调用flush,否则最后一部分数据可能不会被写入
bufferedSink.flush();
}
/**
* 写入,回调进度接口
* @param sink
* @return
*/
private Sink sink(Sink sink){
return new ForwardingSink(sink) {
//当前写入字节数
long bytesWritten = 0L;
//总字节长度,避免多次调用contentLength()方法
long contentLength = 0L;
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
if(contentLength == 0){
//获得contentLength的值,后续不再调用
contentLength = contentLength();
}
//增加当前写入的字节数
bytesWritten += byteCount;
//回调
callback.onLoading(file , contentLength, bytesWritten);
}
};
}
}
retrofit中callback不能完全满足我们的需求,我们也可以进一步扩展
public abstract class RetrofitCallback<T> implements Callback<T>{
@Override
public void onResponse(Call<T> call, Response<T> response) {
if(response.isSuccessful()){
onSuccess(call, response);
}else{
onFailure(call, new Throwable(response.message()));
}
}
public abstract void onSuccess(Call<T> call , Response<T> response);
public void onLoading(File file , long total , long progress){
}
}
4、最终使用
使用起来用到的代码依旧相同,那我们在次提取封装
public class UploadFileUitl{
public UploadFileUitl(String url , Map<> paramMap , List mList , RetrofitCallBack callback){
List<MultipartBody.Part> parts = MultipartBuilder.filesToParts(mList , callback);//创建Multipart
//创建Retrofit,并调用enqueue实现异步操作
RetrofitBuilder.buildRetrofit(url)
.create(FileUploadService.class)'
.uploadFilesWithParts(parts , paramMap)
.enqueue(callback);}
}
ok,开始最终的调用吧
new UploadFileUtil(url , paramMap , fileList , new RetrofitCallback(){
//成功
@Override
public void onSuccess(Call call , Response response){}
//失败
@Override
public void onFailure(Call call , Throwable t){}
//加载中
@Override
public void onLoading(File file , long total , long progress){}
});
不要忘记android的规则,对一些耗时操作不要放在主线程中。因此我们还需要将最终调用放到子线程中。我用的是AsyncTask
class UploadTask extends AsyncTask<Map , Map , Object>{
//在执行过程中调用上传监听过程
@Override
protected Object doInBackground(Map... params){
new UploadFileUtil(url , paramMap , fileList , new RetrofitCallback(){
//成功
@Override
public void onSuccess(Call call , Response response){}
//失败
@Override
public void onFailure(Call call , Throwable t){}
//加载中
@Override
public void onLoading(File file , long total , long progress){
....
publishProgress(map);//出发处理进度
}
});
}
//处理进度
@Override
protected void onProgressUpdate(Map... maps){
}
}