Android 移动开发当中免不了要访问网络,访问网络的方式相信每个人都会有自己不同的方法,你可以自己封装网络请求,也可以用网上别人开源的框架。如果开源的框架当中,目前最受欢迎的可要数Retrofit了,而使用Retrofit中最麻烦也就是上传图片或者文件了,本文就对如何使用Retrofit同时上传多参数和多图片文件做详细的演示,并贴出相应实例代码。
在正式本文的讲解之前,先说明一下,基于现在RxJava也火得不要不要的,所以本文就结合RxJava和Retrofit一起来说明他的用法。
1.第一步:导包
现在的导包方式基本都使用Maven自动导包了,所以我们选择下面的方式而不用再去下载jar然后再导入。在Module的build.gradle文件中添加如下的依赖
compile ‘io.reactivex:rxjava:1.2.9’
compile ‘io.reactivex:rxandroid:1.2.1’
compile ‘com.squareup.retrofit2:adapter-rxjava:2.0.0’
compile ‘com.squareup.retrofit2:retrofit:2.2.0’
compile ‘com.squareup.retrofit2:converter-gson:2.2.0’
compile ‘com.squareup.okhttp3:okhttp:3.6.0’
2.第二步:构造你的Retrofit公共管理者
package com.yuanyi.logistics.manager;
import com.yuanyi.logistics.constant.Constants;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;/**
* Created by AHuan on 2017/4/24.
*/public class RetrofitManager {
private static RetrofitManager mRetrofitManager;
private Retrofit mRetrofit;
private RetrofitManager(){
initRetrofit();
}
//获取同步静态对象
public static synchronized RetrofitManager getInstance(){
if(mRetrofitManager==null){
mRetrofitManager=new RetrofitManager();
}
return mRetrofitManager;
}
private void initRetrofit(){
HttpLoggingInterceptor httpLoggingInterceptor=new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder=new OkHttpClient.Builder();
//添加过滤器
builder.addInterceptor(httpLoggingInterceptor);
//设置连接超时时间
builder.connectTimeout(15, TimeUnit.SECONDS);
//设置读取超时时间
builder.readTimeout(20,TimeUnit.SECONDS);
//设置写超时时间
builder.writeTimeout(20,TimeUnit.SECONDS);
//设置超时重连
builder.retryOnConnectionFailure(true);
OkHttpClient client=builder.build();
mRetrofit=new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(client)
.build();
}
public <T> T creatReq(Class<T> reqServer){
return mRetrofit.create(reqServer);
}
}
3.编写请求接口
package com.yuanyi.logistics.network.api;
import com.yuanyi.logistics.bean.AppUpdateBean;
import com.yuanyi.logistics.bean.ResponseBean;
import com.yuanyi.logistics.bean.StorageBean;
import com.yuanyi.logistics.bean.TaskBean;
import com.yuanyi.logistics.bean.User;import java.util.List;
import java.util.Map;import okhttp3.RequestBody;
import retrofit2.http.Field;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.PartMap;
import retrofit2.http.Query;
import rx.Observable;/**
* Created by AHuan on 2017/4/22.
*/public interface TaskApi {
//获取司机的任务列表
/**
* 使用post方法,查询参数声明字段是userId
* @param userId
* @return
*/
@FormUrlEncoded
@POST("vehicleInspectionApi/find")
Observable<TaskBean> getTask(@Field("userId") String userId );
//获取正在任务中的司机列表
@POST("billListApi/getBydriverUserId")
Observable<List<User>> getDrivers();
//提交问题操作接口
/**
* 实现多参数post请求
* @param dataMap
* @return
*/
@FormUrlEncoded
@POST("billListApi/ updateProblemGoods")
Observable<ResponseBean> updateProblemGoods(@FieldMap Map<String,String> dataMap);
//检测版本更新信息
@POST("editionApi/editionNo")
Observable<AppUpdateBean> getUpdateInfo();
//获取搬仓的货物信息
@FormUrlEncoded
@POST("subaru_Parts/storageCarry/getByStorage")
Observable<StorageBean> getStorageInfo(@Field("partNo") String partNo);
//提交搬仓上传的货物信息
@FormUrlEncoded
@POST("subaru_Parts/storageCarry/update")
Observable<ResponseBean> submitStorage(@FieldMap Map<String,String> dataMap);
//上传加油数据
/**
* 实现多图片文件多参数上传
* @param params
* @return
*/
@Multipart
@POST("vehicleInspectionApi/updateOliApi")
Observable<ResponseBean> uploadAddOil(@PartMap Map<String, RequestBody> params);
/**
* get方法,@Query("Id")表示在Url后面追加字段是id的内容,后面的如此类推
* @param id
* @param passwords
* @param Pass
* @return
*/
@FormUrlEncoded
@GET("userApi/updateApiPass")
Observable<ResponseBean> changePw(@Query("id") int id, @Query("passwords") String passwords, @Query("Pass") String Pass);
}
4.编写工具类
编写一个专门用来生成Map的工具类。
我们以上面中的 Observable uploadAddOil();这个方法为例。
package com.yuanyi.logistics.utils;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;import okhttp3.MediaType;
import okhttp3.RequestBody;/**
* Created by AHuan on 2017/12/11.
* 生成Retrofit的多参数和多文件多图片的构造参数map
*/public class RetrofitRequestBodyUtil {
/**
*使用的时候传进来的Map的构造为Map<String,Object>,如果传的是普通字符串参数的话,就往map里面put<String,String>
*如果传递的是图片或者的话,就put<String,File[]>, key对应的是服务端所要的参数字段名,File[] 所对应的是上传的文件数组
* 例如服务器需要的是下面的参数 @RequestParam(value = "files") MultipartFile[] postedFiles
* 我们的map中就需要put("files",File[]);
* @param resourceMap
* @return
*/
public static Map<String, RequestBody> getRequestBodyMap(Map<String,Object> resourceMap){
Map<String, RequestBody> params=new HashMap<>();
Iterator iterator=resourceMap.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry entry= (Map.Entry) iterator.next();
if(entry.getValue() instanceof String){
//判断值如果是String的话就直接put进去
RequestBody body=RequestBody.create(MediaType.parse("text/plain;charset=UTF-8"),(String)entry.getValue());
params.put((String)entry.getKey(),body);
}else if(entry.getValue() instanceof File){
//判断当前值是单个文件的话就把key设置成服务端所要的参数子端名加文件名,具体格式可以看下面的
RequestBody body=RequestBody.create(MediaType.parse("multipart/form-data;charset=UTF-8"),(File)entry.getValue());
params.put((String)entry.getKey()+"\";filename=\""+((File)entry.getValue()).getName()+"",body);
}else if(entry.getValue() instanceof File[]){
//判断当前的值是文件数组的话,要把一个个文件拆开再put进去
File[] files= (File[]) entry.getValue();
if(files!=null && files.length>0){
for(File f : files){
RequestBody body=RequestBody.create(MediaType.parse("multipart/form-data;charset=UTF-8"),f);
params.put((String)entry.getKey()+"\";filename=\""+f.getName()+"",body);
}
}
}
}
return params;
}
}
5.下面针对上传加油数据接口贴出我的服务端代码
/**
* 添加启程
* 第一个参数为VehicleInspectionVo对象里面的属性,第二个参数是多图片文件上传
* @param vo
* @return
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public ApiResult add(VehicleInspectionVo vo, @RequestParam(value = "file") MultipartFile[] postedFiles) {
ApiResult result = new ApiResult();
String fileName = "";// 返回的图片名称 用,号隔开
// 服务器路径
String path = "/upload/images/vehicleinspection/";
// 物理路径
String realPath = PathUtil.getRealPath(request, path);
// 添加
fileName = fileUploaderController.imageupload(postedFiles, realPath);
vo.setFileName(fileName);
String msg = vehService.addApi(vo);
result.setMsg(msg);
if(result.getMsg().equals("录入成功")){
result.setSuccess(true);
}else{
result.setSuccess(false);
}
return result;
}
6.贴出android端的调用代码(针对上传加油数据接口实现多图片文件多参数上传)
File[] files=new File[2];
final String[] fileNames=new String[2];
for(int i=0;i<2;i++){
fileNames[i]= CreatFileUtil.createFileAndComp(imgList[i]);
files[i]=new File(fileNames[i]);
}
final Map<String,Object> params=new HashMap<>();
params.put("id",task_id+"");
params.put("oilMiles",start_km);
params.put("oil",add_oil_number);
params.put("gasStationAddress",address);
params.put("longitude",mLongitude+"");
params.put("latitude",mLatitude+"");
params.put("file",files);
Map<String, RequestBody> requestBodyMap = RetrofitRequestBodyUtil.getRequestBodyMap(params);
showLoading("信息正在提交");
RetrofitManager.getInstance().creatReq(TaskApi.class)
.uploadAddOil(requestBodyMap)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ResponseBean>() {
@Override
public void onCompleted() {
closeLoading();
}
@Override
public void onError(Throwable e) {
closeLoading();
UIUtil.show("网络出错,数据已缓存至本地");
params.remove("file");
String content=UIUtil.convertRequest(params);
String filePaths="";
for(int i=0;i<fileNames.length;i++){
filePaths=filePaths+";"+fileNames[i];
}
filePaths=filePaths.substring(1,filePaths.length());
UploadBean uploadBean=new UploadBean(Constants.ADD_OIL,content,filePaths);
UploadRequestUtil.saveRequest(uploadBean);
}
@Override
public void onNext(ResponseBean responseBean) {
if (responseBean.success) {
UIUtil.show("加油成功");
finish();
}else{
UIUtil.show(responseBean.msg);
}
}
});