前言
项目中开始使用retrofit2.0作为网络框架,注解的形式使用起来确实简洁。2.0版本相比之前有了比较大的变化,所以屡次在群里看到有童鞋问怎样实现上传下载?肿么获取原始的json?本文主要来回答这些问题。
- 返回原始的json字符串
- 文件上传
- 文件下载
- ......
官网
github项目地址
官方文档
基本的使用姿势(基本配置,get,post请求等,就不重复造轮子了)
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html
实践
建议起码看完retrofit2.0的基本用法再来看本文。
1. 返回原始的的json
场景是这样,服务端返回一个不定类型的json数据,无法确定映射的Gson对象。或者本猿就是要看原汁原味的json肿么办?我依旧使用GsonConverterFactory作为转换器,返回的对象定义为String。。结果不行。。。各种研究+求教后得到解决方案。应该使用ScalarsConverterFactory,关键代码如下
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
api
@GET("user/login")
Call<String> upgrade(@Query("account") String account,
@Query("password") String password,
@Query("callback") String callback);
现在的问题肯定是ScalarsConverterFactory哪来的?其实官方的项目中就有,地址点我。
打开红色箭头指向的文件夹,将retrofit-master\retrofit-converters\scalars\src\main\java\retrofit2\converter\scalars这个目录下的三个文件放到自己的工程下就可以使用了。
回调的string就是原始的json字符串,当然不是json的字符串也可以。为了方便大家,这3个文件直接贴在下面的“下载”模块了,直接copy。
2. 上传
上传的场景是一个表单中既有文本信息又有图片上传,当然单一的就更简单了,去掉相关的参数就行了。
@Multipart
@POST("user/register")
Call<ResponseJson> register(@Part("key1") RequestBody param1,
@Part("key2") RequestBody param2,
@Part("key3\"; filename=\"fileName.png") RequestBody param3
);
key的对应post请求中的key,根据接口文档中的定义自行切换。第三个参数对应的是要上传的图片,注意@Part("key3"; filename="fileName.png") 中key3是对应post中的key,而fileName.png需要替换成自己的文件名称。接着构建参数。
RequestBody param1= RequestBody.create(MediaType.parse("text/plain"), account);
RequestBody param2= RequestBody.create(MediaType.parse("text/plain"), password);
RequestBody imageParam3= RequestBody.create(MediaType.parse("image/*"), file);
account, password是页面传入的参数,注意第三个参数也就是图片上传的参数,传入一个File对象,可以是SD卡中的图片。将这些参数扔进前面的register方法,请求就发成功了。这里只写了实现的核心点,如果需要详细实现示例,请留言。
3. 下载
从服务器下载一个文件,可能你还需要显示进度条。别急,本猿这些统统有。retrofit老惯例,先定义api。
/**
* 下载文件
*/
@GET("{fileName}")
Call<ResponseBody> getFile(@Path("fileName")String fileName);
注意这里的返回的是ResponseBody对象,这是okhttp中的对象。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
APIService apiService = retrofit.create(APIService.class);
Call<ResponseBody> call = apiService.getFile(fileName);
call.enqueue(callback);
这里的baseUrl就是是下载的地址,但是baseUrl是以'/'结尾的url,如果你的下载地址是一个完整的url的话,你需要截取字符串,把后面一段url作为fileName参数拼接上去。如“http://download/1.png”,截取成“http://download/” + “1.png”,前一段是baseUrl,后面是fileName。这里的ScalarsConverterFactory要处理下,才能返回ResponseBody对象。
public final class ScalarsConverterFactory extends Converter.Factory {
public static ScalarsConverterFactory create() {
return new ScalarsConverterFactory();
}
private ScalarsConverterFactory() {
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (type == String.class
|| type == boolean.class
|| type == Boolean.class
|| type == byte.class
|| type == Byte.class
|| type == char.class
|| type == Character.class
|| type == double.class
|| type == Double.class
|| type == float.class
|| type == Float.class
|| type == int.class
|| type == Integer.class
|| type == long.class
|| type == Long.class
|| type == short.class
|| type == Short.class
|| type == ResponseBody.class
) {
return ScalarRequestBodyConverter.INSTANCE;
}
return null;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == String.class) {
return StringResponseBodyConverter.INSTANCE;
}
if (type == Boolean.class || type == boolean.class) {
return BooleanResponseBodyConverter.INSTANCE;
}
if (type == Byte.class || type == byte.class) {
return ByteResponseBodyConverter.INSTANCE;
}
if (type == Character.class || type == char.class) {
return CharacterResponseBodyConverter.INSTANCE;
}
if (type == Double.class || type == double.class) {
return DoubleResponseBodyConverter.INSTANCE;
}
if (type == Float.class || type == float.class) {
return FloatResponseBodyConverter.INSTANCE;
}
if (type == Integer.class || type == int.class) {
return IntegerResponseBodyConverter.INSTANCE;
}
if (type == Long.class || type == long.class) {
return LongResponseBodyConverter.INSTANCE;
}
if (type == Short.class || type == short.class) {
return ShortResponseBodyConverter.INSTANCE;
}
if(type == ResponseBody.class){
return ScalarResponseBodyConverter.INSTANCE;
}
return null;
}
}
final class ScalarResponseBodyConverters {
private ScalarResponseBodyConverters() {
}
static final class StringResponseBodyConverter implements Converter<ResponseBody, String> {
static final StringResponseBodyConverter INSTANCE = new StringResponseBodyConverter();
@Override public String convert(ResponseBody value) throws IOException {
String valueStr = value.string();
return valueStr;
}
}
static final class BooleanResponseBodyConverter implements Converter<ResponseBody, Boolean> {
static final BooleanResponseBodyConverter INSTANCE = new BooleanResponseBodyConverter();
@Override public Boolean convert(ResponseBody value) throws IOException {
return Boolean.valueOf(value.string());
}
}
static final class ByteResponseBodyConverter implements Converter<ResponseBody, Byte> {
static final ByteResponseBodyConverter INSTANCE = new ByteResponseBodyConverter();
@Override public Byte convert(ResponseBody value) throws IOException {
return Byte.valueOf(value.string());
}
}
static final class CharacterResponseBodyConverter implements Converter<ResponseBody, Character> {
static final CharacterResponseBodyConverter INSTANCE = new CharacterResponseBodyConverter();
@Override public Character convert(ResponseBody value) throws IOException {
String body = value.string();
if (body.length() != 1) {
throw new IOException(
"Expected body of length 1 for Character conversion but was " + body.length());
}
return body.charAt(0);
}
}
static final class DoubleResponseBodyConverter implements Converter<ResponseBody, Double> {
static final DoubleResponseBodyConverter INSTANCE = new DoubleResponseBodyConverter();
@Override public Double convert(ResponseBody value) throws IOException {
return Double.valueOf(value.string());
}
}
static final class FloatResponseBodyConverter implements Converter<ResponseBody, Float> {
static final FloatResponseBodyConverter INSTANCE = new FloatResponseBodyConverter();
@Override public Float convert(ResponseBody value) throws IOException {
return Float.valueOf(value.string());
}
}
static final class IntegerResponseBodyConverter implements Converter<ResponseBody, Integer> {
static final IntegerResponseBodyConverter INSTANCE = new IntegerResponseBodyConverter();
@Override public Integer convert(ResponseBody value) throws IOException {
return Integer.valueOf(value.string());
}
}
static final class LongResponseBodyConverter implements Converter<ResponseBody, Long> {
static final LongResponseBodyConverter INSTANCE = new LongResponseBodyConverter();
@Override public Long convert(ResponseBody value) throws IOException {
return Long.valueOf(value.string());
}
}
static final class ShortResponseBodyConverter implements Converter<ResponseBody, Short> {
static final ShortResponseBodyConverter INSTANCE = new ShortResponseBodyConverter();
@Override public Short convert(ResponseBody value) throws IOException {
return Short.valueOf(value.string());
}
}
static final class ScalarResponseBodyConverter implements Converter<ResponseBody, ResponseBody> {
static final ScalarResponseBodyConverter INSTANCE = new ScalarResponseBodyConverter();
@Override public ResponseBody convert(ResponseBody value) throws IOException {
return value;
}
}
}
final class ScalarRequestBodyConverter<T> implements Converter<T, RequestBody> {
static final ScalarRequestBodyConverter<Object> INSTANCE = new ScalarRequestBodyConverter<>();
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain; charset=UTF-8");
private ScalarRequestBodyConverter() {
}
@Override public RequestBody convert(T value) throws IOException {
return RequestBody.create(MEDIA_TYPE, String.valueOf(value));
}
}
这3个类请直接贴到自己的工程里。在callback里我们可以拿到原始的ResponseBody。
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
ResponseBody responseBody = response.body();
InputStream inputStream = responseBody.byteStream();
if (inputStream != null) {
totalSize = (int) responseBody.contentLength();
saveFileTask(inputStream);
}
}
有了文件流和文件大小,相信机智的大家可以搞定下载和进度条了吧。
后话
还有些retrofit2.0的使用,比如搞定cookie,拦截器什么的如果大家需要请留言,其他需求也可以留言讨论。如果觉得有用请帮忙戳喜欢。。。
网友评论
可不可以讲解一下比如@Get,@Post类似的注解?