您的位置:首页 > 编程语言 > Java开发

Retrofit2.0和Rxjava结合使用的简单记录

2017-07-26 00:55 483 查看

Gradle的配置(该配置是针对rxjava1.x的)

compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
//okhttp log 工具
compile 'com.squareup.okhttp3:logging-interceptor:3.1.2'
compile 'com.google.code.gson:gson:2.5'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'

##常用的注解

@Path:该注解写在方法中的参数类型(String)前面;用于指定@POST()或者@GET()请求中括号中的参数;该参数中不允许存在 斜杠 , 即只能表示一级,类似"目录";
可以把所有的路径都存在一个类中,在@Post()的括号中引用这个路径,这样@Path就不需要使用;除非相同路径下都是最后一级不相同,可以使用该注解。

@ Url :该注解写在方法的参数类型前面;表示完整的Url。

@Query:用于自动拼接URL ?id=3&name=zhangsan&age=24;Post和Get请求中都可以使用

@QueryMap :传入一个Map类型,自动拼接URL;Post和Get请求中都可以使用

@FormUrlEncoded 和Post一起使用;提交没有文件的表单数据;和参数注解@Field和@FieldMap配合使用

@ Field:用于POST请求中,输入拼接的请求体键值对;不带文件上传的表单提交

@ FieldMap :用于POST请求中,输入拼接的请求体键值对的Map对象;不带文件上传的表单提交

@Streaming:下载文件必须要加上这个,保证下载的内容不会一下子都加载到内存中去;表示和服务器是长连接,会边下载流,边处理(比如写入存储器中),使得内存不会有一直持有所有的流数据。

@Multipart:提交带有文件的表单使用;配合参数注解@Part和@PartMap使用;@PartMap的类型是
<String,RequestBody>

@Part,@PartMap: 用于POST文件上传
@PartMap接受的数据类型是HashMap<String,RequestBody>
@Part用法:@Part("xxx") RequestBody body

@Body 如果上传的内容不是用@MultiPart来修饰的,也不是要传递键值对。参数中不能用@Part或者@PartMap来修饰。此时选择用@Body;比如传递一个json字符串给服务器,此时就需要body,还需要改一下Header

关于文件的上传:
参见 :/detail/2687595889.html
文件的下载可以使用 RxDownload

@Body:POST请求中的参数

@RequestBody

okhttp3.Media的常见type类型

eg: RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), jsonString);

text/html : HTML格式
text/plain :纯文本格式

text/xml : XML格式
image/gif :gif图片格式

image/jpeg :jpg图片格式
image/png:png图片格式
以application开头的媒体格式类型:

application/xhtml+xml :XHTML格式
application/xml : XML数据格式
application/atom+xml :Atom XML聚合格式

application/json : JSON数据格式
application/pdf :pdf格式

application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式

@Headers :注解设置固定的请求头,所有请求头不会相互覆盖,即使名字相同。

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
// 注意Headers括号中的键值对,冒号后面有一个空格!!!!
@Headers({ "Accept: application/vnd.github.v3.full+json","User-Agent: Retrofit-Sample-App"})
@GET("users/{username}")Call<User> getUser(@Path("username") String username);


@Header
使用 @Header 注解动态更新请求头,匹配的参数必须提供给 @Header ,若参数值为 null ,这个头会被省略,否则,会使用参数值的 toString 方法的返回值。

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

获取Retrofit的实例

public class RetrofitClient {

private static final String TAG = "RetrofitClient";

//    private static final String BASE_URL ="https://api.douban.com/v2/movie/";

private static OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//                    Log.v(TAG,chain.connection().route().address().toString());
Log.v(TAG,chain.request().url().toString());
Request.Builder builder = chain.request().newBuilder();
//添加共同的头信息
//                    builder.addHeader("token", "abc");
return chain.proceed(builder.build());
}
})
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build();

private static class Holder{
private static Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(StringConverterFactory.create())
.baseUrl(UrlConst.BASE_URL)
.client(client)
.build();
}

public static Retrofit getRetrofitInstance() {
return Holder.retrofit;
}

private static Retrofit retrofit2;

public static Retrofit getStringRetrofitInstance(String url) {

retrofit2 = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(StringConverterFactory.create())
.baseUrl(url)
.client(client)
.build();
return retrofit2;
}

public static Retrofit retrofit3;

public static Retrofit getRetrofitInstance(String url)
{
retrofit3 = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(StringConverterFactory.create())
.baseUrl(url)
.client(client)
.build();
return retrofit3;
}
}

创建Service的接口

GET请求

//具体的网络url : 举例子:https://api.douban.com/v2/movie/top250?start=2&count=4
public interface ApiService {

@GET("top250")
Observable<Entity> getMovies(@Query("start") int start,@Query("count") int count);
}

// 最好这么写,参数用变量代替
public interface ApiService {

@GET("{url}")
Observable<Entity> getMovies(@Path("url") String url, @Query("start") int start, @Query("count") int count);
}

POST请求

public interface MovieService {
//获取豆瓣Top250 榜单
@FormUrlEncoded
@POST("top250")
Observable<MovieSubject> getTop250(@Field("start") int start, @Field("count") int count);
}

关于Post请求需要注意的几个地方:
必须要有@FormUrlEncoded 配合 Filed使用时

下载文件的GET请求

//必须要加上@Streaming,不然会把下载的东西全部读到内存中去
@Streaming
@GET
Observable<ResponseBody> downloadFile(@Url String url);

写入ReponseBody到文件系统

/**
* 将ResponseBody中的内容保存到本地路径 ;该路径必须已经存在
*
* @param body
* @param localPath
*/
public static void writeResponseBodyToDisk(ResponseBody body, String localPath) {
File file = new File(localPath);

InputStream is = null;

OutputStream os = null;

long totalLength = body.contentLength();
long currentLength = 0;
double rate = 0;

byte[] bytes = new byte[4096];

try {
os = new FileOutputStream(file);
is = body.byteStream();
int length = -1;
while ((length = is.read(bytes)) != -1) {
os.write(bytes, 0, length);
currentLength += length;
rate = currentLength * 1.0 / totalLength;
}

os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {

try {
if (is != null) {
is.close();
}

if (os != null) {
os.close();
}

} catch (IOException e) {
e.printStackTrace();
}
}

}

踩过的坑:

1.Post请求中

//获取 任务
@POST("index.php/machine/{url}")
Observable<String> getTask(@Path("url") String url, @Body RequestBody requestBody);

注解Post的括号中如果有引用方法的url时,那么方法中的url不能包括斜杠 / ,也不能包括问号 ?
因为斜杠 / 会被转义为 %2F ; ? 会被转义为 %3F ;斜杠和问号只有写在@POST的括号中才不会被转义。

2.Post请求上传多个文件和其他字符串参数
文件上传可以用两种方式来做:
1.使用MultipartBody.Part 类型修饰在函数的文件参数前面;文件的参数和字符串参数分开来做;字符串参数可以用@PartMap来包裹

RequestBody apkBody = RequestBody.create(MediaType.parse("multipart/form-data"),file_apk);
MultipartBody.Part partApk = MultipartBody.Part.createFormData("apk_url",file_apk.getName(),apkBody);

2.(用的少)使用@PartMap注解修饰参数类型前面 , 上传多个文件,但是对创建文件的RequestBody做一步处理;这种方式其实是把多个文件和字符串都放在@PartMap修饰的HasMap<String,RequestBody>中即:

RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
RequestBody requestBody1 = RequestBody.create(MediaType.parse("multipart/form-data"), file1);
Map<String, RequestBody> params = new HashMap<>();

params.put(key+"\"; filename=\""+ file.getName(), requestBody);
params.put(key1+"\"; filename=\""+ file1.getName(), requestBody1);

3.如果服务器返回值不是一个json,而是一个字符串String。那么我们用的GsonConvertory的Retrofit就不可用了。需要自定义一个StringConvertory.

这种上传方式没有尝试过:

MultipartBody.Part photo1part =
MultipartBody.Part.createFormData("pic1", "pic2", requestBody1);

@Multipart
@POST("upload.php")
Observable<ResponseBody> uploadFile(
@Part() List<MultipartBody.Part > files );

5.文件和参数同时上传的时候会遇到参数的value如果是String,会有两个双引号在外面。原因是由于使用了
Map<String,String>的方式,可以使用Map<String,RequestBody>的方式来解决这个问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: