Android Retrofit实现多图片/文件、图文上传功能
什么是 retrofit ?
retrofit是square开发的一个android和java的rest客户端库。这个库非常简单并且具有很多特性,相比其他的网络库,更容易让初学者快速掌握。它可以处理get、post、put、delete…等请求,还可以使用picasso加载图片。
一、再次膜拜下retrofit
retrofit无论从性能还是使用方便性上都很屌!!!,本文不去介绍其运作原理(虽然很想搞明白),后面会出专题文章解析retrofit的内部原理;本文只是从使用上解析retrofit实现多图片/文件、图文上传的功能。
二、概念介绍
1)注解@multipart
从字面上理解就是与多媒体文件相关的,没错,图片、文件等的上传都要用到该注解,其中每个部分需要使用@part来注解。。看其注释
/** * denotes that the request body is multi-part. parts should be declared as parameters and * annotated with {@link part @part}. */
2)注解@partmap
当然可以理解为使用@partmap注释,传递多个part,以实现多文件上传。注释
/** * denotes name and value parts of a multi-part request. * <p> * values of the map on which this annotation exists will be processed in one of two ways: * <ul> * <li>if the type is {@link okhttp3.requestbody requestbody} the value will be used * directly with its content type.</li> * <li>other object types will be converted to an appropriate representation by using * {@linkplain converter a converter}.</li> * </ul> * <p> * <pre><code> * @multipart * @post("/upload") * call<responsebody> upload( * @part("file") requestbody file, * @partmap map<string, requestbody> params); * </code></pre> * <p> * a {@code null} value for the map, as a key, or as a value is not allowed. * * @see multipart * @see part */
3)requestbody
从上面注释中就可以看到参数类型是requestbody,其就是请求体。文件上传就需要参数为requestbody。官方使用说明如下
multipart parts use one of retrofit's converters or they can implement requestbody to handle their own serialization.
四、基本实现
了解了以上概念,下面就一一实现
1)接口定义
public interface ihttpservice { @multipart @post("nocheck/file/agree.do") call<basebean> uploadagree(@partmap map<string, requestbody>params); }
basebean是根据服务端返回数据进行定义的,这个使用时可以根据自有server定义。
2)retrofit实现
/** * created by dell on 2017/3/16. * 上传文件用(包含图片) */ public class retrofithttpupload { /** * 超时时间60s */ private static final long default_timeout = 60; private volatile static retrofithttpupload minstance; public retrofit mretrofit; public ihttpservice mhttpservice; private map<string, requestbody> params = new hashmap<string, requestbody>(); private retrofithttpupload() { mretrofit = new retrofit.builder() .baseurl(urlconfig.root_url) .client(genericclient()) .addconverterfactory(gsonconverterfactory.create()) .build(); mhttpservice = mretrofit.create(ihttpservice.class); } public static retrofithttpupload getinstance() { if (minstance == null) { synchronized (retrofithttpupload.class) { if (minstance == null) minstance = new retrofithttpupload(); } } return minstance; } /** * 添加统一超时时间,http日志打印 * * @return */ public static okhttpclient genericclient() { httplogginginterceptor logging = new httplogginginterceptor(); logging.setlevel(httplogginginterceptor.level.body); okhttpclient httpclient = new okhttpclient.builder() .addinterceptor(logging) .connecttimeout(default_timeout, timeunit.seconds) .writetimeout(default_timeout, timeunit.seconds) .readtimeout(default_timeout, timeunit.seconds) .build(); return httpclient; } /** * 将call加入队列并实现回调 * * @param call 调入的call * @param retrofitcallback 回调 * @param method 调用方法标志,回调用 * @param <t> 泛型参数 */ public static <t> void addtoenqueue(call<t> call, final retrofitcallback retrofitcallback, final int method) { final context context = myapplication.getcontext(); call.enqueue(new callback<t>() { @override public void onresponse(call<t> call, response<t> response) { logutil.d("retrofit back code ====" + response.code()); if (null != response.body()) { if (response.code() == 200) { logutil.d("retrofit back body ====" + new gson().tojson(response.body())); retrofitcallback.onresponse(response, method); } else { logutil.d("toenqueue, onresponse fail:" + response.code()); toastutil.makeshorttext(context, "网络连接错误" + response.code()); retrofitcallback.onfailure(response, method); } } else { logutil.d("toenqueue, onresponse fail m:" + response.message()); toastutil.makeshorttext(context, "网络连接错误" + response.message()); retrofitcallback.onfailure(response, method); } } @override public void onfailure(call<t> call, throwable t) { logutil.d("toenqueue, onresponse fail unknown:" + t.getmessage()); t.printstacktrace(); toastutil.makeshorttext(context, "网络连接错误" + t.getmessage()); retrofitcallback.onfailure(null, method); } }); } /** * 添加参数 * 根据传进来的object对象来判断是string还是file类型的参数 */ public retrofithttpupload addparameter(string key, object o) { if (o instanceof string) { requestbody body = requestbody.create(mediatype.parse("text/plain;charset=utf-8"), (string) o); params.put(key, body); } else if (o instanceof file) { requestbody body = requestbody.create(mediatype.parse("multipart/form-data;charset=utf-8"), (file) o); params.put(key + "\"; filename=\"" + ((file) o).getname() + "", body); } return this; } /** * 构建requestbody */ public map<string, requestbody> bulider() { return params; } }
其中定义了retrofit实例、还用拦截器定义了统一的超时时间和日志打印;将call加入队列并实现回调。最重要的就是添加参数:
/** * 添加参数 * 根据传进来的object对象来判断是string还是file类型的参数 */ public retrofithttpupload addparameter(string key, object o) { if (o instanceof string) { requestbody body = requestbody.create(mediatype.parse("text/plain;charset=utf-8"), (string) o); params.put(key, body); } else if (o instanceof file) { requestbody body = requestbody.create(mediatype.parse("multipart/form-data;charset=utf-8"), (file) o); params.put(key + "\"; filename=\"" + ((file) o).getname() + "", body); } return this; }
这里就是根据传入的参数,返回不同的requestbody。
3)使用
private void uploadagree() { showwaitdialog(); retrofithttpupload retrofithttpupload = retrofithttpupload.getinstance(); if (!stringutil.isempty(pathimage[0])){ retrofithttpupload = retrofithttpupload.addparameter("pic1",new file(pathimage[0])); } if (!stringutil.isempty(pathimage[1])){ retrofithttpupload = retrofithttpupload.addparameter("pic2", new file(pathimage[1])); } if (!stringutil.isempty(pathimage[2])){ retrofithttpupload = retrofithttpupload.addparameter("zip", new file(pathimage[2])); } map<string, requestbody> params = retrofithttpupload .addparameter("status", "4") .addparameter("pickupid", tv_orderquality_pid.gettext().tostring()) .addparameter("cause", reason) .addparameter("connectname", et_orderquality_lxrname.gettext().tostring()) .addparameter("connectphone", et_orderquality_lxrphone.gettext().tostring()) .addparameter("details", et_orderquality_xqms.gettext().tostring()) .bulider(); retrofithttpupload.addtoenqueue(retrofithttpupload.getinstance().mhttpservice.uploadagree(params), this, httpstaticapi.http_uploadagree); }
需要注意的是要对图片及文件路径进行判空操作,负责会报异常w/system.err: java.io.filenotfoundexception: /: open failed: eisdir (is a directory)
以上所述是小编给大家介绍的android基于retrofit实现多图片/文件、图文上传功能,希望对大家有所帮助