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);
                    }
                }
            });