Retrofit2 学习笔记(一)
2016-07-27 15:17
399 查看
一 Retrofit 2 简单使用的粗略介绍
环境配置
简单封装 ServiceGenerator
Converter
怎么获取返回的Header和响应码
Log Requests and Responses
同步请求 和 异步请求Synchronous and Asynchronous Requests
Cancel Requests
二 Retrofit 2 注解详解
作用于方法
1添加请求头 Headers
2表示响应体的数据用流的形式返回 Streaming
不使用Streaming的文件下载
使用Streaming的文件下载 大文件的下载
作用于方法参数形参
1用作添加不固定值的 Header
2用作非表单请求体 Body
3用作表单字段 Field FieldMap Part PartMap
FormUrlEncoded
Multipart
4用于URL Path QueryQueryMap Url
Path
Query
QueryMap
基本使用
QueryMap Options encoded
Url
5小注意 补充
QueryField和Part这三者都支持数组和实现了Iterable接口的类型如ListSet等方便向后台传递数组
如何在OkHttp Interceptor 中管理 Headers
如何添加Query参数 给每个请求
Url拼接规范
使用:
在默认情况下
使用
Log Level:
None
Basic
Headers
Body
在
1. 如果要进行同步请求就调用
2. 如果要进行异步请求就调用
检查是否已经取消的判断
(1)添加请求头 –>
(2)表示响应体的数据用流的形式返回 –>
如果没有该注解,默认会把数据全部载入内存,之后你通过流获取数据也不过是读取内存中的数据,所以如你的返回的数据比较大,你就需要使用这个注解
这个可以用
①. 不使用Streaming的文件下载
调用请求
保存文件到Disk
②. 使用Streaming的文件下载, 大文件的下载
需要注意的是,如果使用了
(1)用作添加不固定值的 –>
(2)用作非表单请求体 –>
被
(3)用作表单字段 –>
用于表单字段
非
非
①. FormUrlEncoded
表示请求体是一个
②. Multipart
表示请求体是一个支持文件上传的
(4)用于URL –>
①. Path
②. Query
③. QueryMap
基本使用
QueryMap Options : encoded
在请求的
④. Url
使用
Retrofit 2 使用 OkHttp的 HttpUrl 来解析每个 Url的端点 就像 网站上的
环境配置
简单封装 ServiceGenerator
Converter
怎么获取返回的Header和响应码
Log Requests and Responses
同步请求 和 异步请求Synchronous and Asynchronous Requests
Cancel Requests
二 Retrofit 2 注解详解
作用于方法
1添加请求头 Headers
2表示响应体的数据用流的形式返回 Streaming
不使用Streaming的文件下载
使用Streaming的文件下载 大文件的下载
作用于方法参数形参
1用作添加不固定值的 Header
2用作非表单请求体 Body
3用作表单字段 Field FieldMap Part PartMap
FormUrlEncoded
Multipart
4用于URL Path QueryQueryMap Url
Path
Query
QueryMap
基本使用
QueryMap Options encoded
Url
5小注意 补充
QueryField和Part这三者都支持数组和实现了Iterable接口的类型如ListSet等方便向后台传递数组
如何在OkHttp Interceptor 中管理 Headers
如何添加Query参数 给每个请求
Url拼接规范
一. Retrofit 2 简单使用的粗略介绍
1. 环境配置
Retrofit 2默认使用
Okhttp作为他的网络层,所以不需要再另外导
okhttp的包
build.gradle
dependencies { // Retrofit & OkHttp compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' }
2. 简单封装 —》 ServiceGenerator
public class ServiceGenerator { public static final String API_BASE_URL = "http://your.api-base.url"; private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); private static Retrofit.Builder builder = new Retrofit.Builder() .baseUrl(API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()); public static <S> S createService(Class<S> serviceClass) { Retrofit retrofit = builder.client(httpClient.build()).build(); return retrofit.create(serviceClass); } }
使用:
public interface GitHubClient { @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo ); } static class Contributor { String login; int contributions; } public static void main(String... args) { // Create a very simple REST adapter which points the GitHub API endpoint. GitHubClient client = ServiceGenerator.createService(GitHubClient.class); // Fetch and print a list of the contributors to this library. Call<List<Contributor>> call = client.contributors("fs_opensource", "android-boilerplate"); try { List<Contributor> contributors = call.execute().body(); } catch (IOException e) { // handle errors } for (Contributor contributor : contributors) { System.out.println( contributor.login + " (" + contributor.contributions + ")"); } }
3. Converter
我们想来看看有哪些Converter
compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:converter-jackson:2.1.0' compile 'com.squareup.retrofit2:converter-moshi:2.1.0' compile 'com.squareup.retrofit2:converter-protobuf:2.1.0' compile 'com.squareup.retrofit2:converter-wire:2.1.0' compile 'com.squareup.retrofit2:converter-simplexml:2.1.0' compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
在默认情况下
Retrofit只支持将
HTTP的响应体转换换为
ResponseBody,所以如果没有引入
Gson的支持的话,返回值就这样写
Call<ResponseBody>,但如果响应体只是支持转换为
ResponseBody的话为何要引用泛型呢?返回值直接用一个
Call就行了嘛,既然支持泛型,那说明泛型参数可以为其他类型的,而
Converter就是Retrofit为我们提供于将
ResponseBody转换为我们想要的类型,有了
Converter之后我们可以把接口写成这个样子:
public interface BlogService { @GET("blog/{id}") //这里的{id} 表示是一个变量 Call<Blog> getFirstBlog(/** 这里的id表示的是上面的{id} */@Path("id") int id); }
怎么获取返回的Header和响应码?
可以使用Call<Response<T>>代替
Call<T>,这里的
Response指
retrofit2.Response
使用
Call<Result<T>>代替
Call<T>,这里的
Result是指
retrofit2.adapter.rxjava.Result,这个
Result中包含了
Response的实例
4. Log Requests and Responses
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); // set your desired log level logging.setLevel(Level.BODY); OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); // add your other interceptors … // add logging as last interceptor httpClient.addInterceptor(logging); // <-- this is the important line! Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(httpClient.build()) .build();
Log Level:
None
Basic
Headers
Body
5. 同步请求 和 异步请求(Synchronous and Asynchronous Requests)
Retrofit支持 同步和异步的请求方式。我们可以通过设置返回类型的服务方法来定义具体的执行。
public interface TaskService { @GET("/tasks") Call<List<Task>> getTasks(); }
在
Retrofit 2中,每个请求都被转为Call对象,
1. 如果要进行同步请求就调用
call.execute().body(),注意如果在主线程调用会报错。自己开个线程
2. 如果要进行异步请求就调用
call.equeue(new Callback<T>...)
6. Cancel Requests
call.cancel(); //发生某些事情,就取消
检查是否已经取消的判断
new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { Log.d(TAG, "request success"); } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { if (call.isCanceled()) { Log.e(TAG, "request was cancelled"); } else { Log.e(TAG, "other larger issue, i.e. no network connection?"); } } };
二. Retrofit 2 注解详解
1. 作用于方法
(1)添加请求头 –> Headers
Headers
public interface UserService { @Headers({ "Accept: application/vnd.yourapi.v1.full+json", "User-Agent: Your-App-Name" }) @GET("/tasks/{task_id}") Call<Task> getTask(@Path("task_id") long taskId); }
(2)表示响应体的数据用流的形式返回 –> Streaming
如果没有该注解,默认会把数据全部载入内存,之后你通过流获取数据也不过是读取内存中的数据,所以如你的返回的数据比较大,你就需要使用这个注解这个可以用
下载文件来诠释,
①. 不使用Streaming的文件下载
两种方式 ,第二种是 动态URL的形式,后面会介绍 // option 1: a resource relative to your base URL @GET("/resource/example.zip") Call<ResponseBody> downloadFileWithFixedUrl(); // option 2: using a dynamic URL @GET Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);
调用请求
FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class); Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccess()) { Log.d(TAG, "server contacted and has file"); boolean writtenToDisk = writeResponseBodyToDisk(response.body()); Log.d(TAG, "file download was a success? " + writtenToDisk); } else { Log.d(TAG, "server contact failed"); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG, "error"); } });
保存文件到Disk
private boolean writeResponseBodyToDisk(ResponseBody body) { try { // todo change the file location/name according to your needs File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + "Future Studio Icon.png"); InputStream inputStream = null; OutputStream outputStream = null; try { byte[] fileReader = new byte[4096]; long fileSize = body.contentLength(); long fileSizeDownloaded = 0; inputStream = body.byteStream(); outputStream = new FileOutputStream(futureStudioIconFile); while (true) { int read = inputStream.read(fileReader); if (read == -1) { break; } outputStream.write(fileReader, 0, read); fileSizeDownloaded += read; Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize); } outputStream.flush(); return true; } catch (IOException e) { return false; } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } catch (IOException e) { return false; } }
②. 使用Streaming的文件下载, 大文件的下载
@Streaming @GET Call<ResponseBody> downloadFileWithDynamicUrlAsync(@Url String fileUrl);
需要注意的是,如果使用了
@Streaming声明,但是还用了
①的代码的话会报错
android.os.NetworkOnMainThreadException.所以要开个线程
final FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class); new AsyncTask<Void, Long, Void>() { @Override protected Void doInBackground(Void... voids) { Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccess()) { Log.d(TAG, "server contacted and has file"); boolean writtenToDisk = writeResponseBodyToDisk(response.body()); Log.d(TAG, "file download was a success? " + writtenToDisk); } else { Log.d(TAG, "server contact failed"); } } return null; } }.execute();
2. 作用于方法参数(形参)
(1)用作添加不固定值的 –> Header
public interface UserService { @GET("/tasks") Call<List<Task>> getTasks(@Header("Content-Range") String contentRange); }
(2)用作非表单请求体 –> Body
被@Body注解的的
Blog将会被
Gson转换成
RequestBody发送到服务器。
BlogService service = retrofit.create(BlogService.class); Blog blog = new Blog(); blog.content = "新建的Blog"; blog.title = "测试"; blog.author = "怪盗kidou"; Call<Result<Blog>> call = service.createBlog(blog);
结果 Result{code=200, msg='OK', data=Blog{id=20, date='2016-04-21 05:29:58', author='怪盗kidou', title='测试', content='新建的Blog'}, count=0, page=0}
(3)用作表单字段 –> Field
、FieldMap
、 Part
、 PartMap
Field、
FieldMap、
Part、
PartMap
用于表单字段
Field和
FieldMap与
FormUrlEncoded注解配合
Part和
PartMap与
Multipart注解配合,适合有文件上传的情况
FieldMap的接受类型是
Map<String,String>,
非
String类型会调用其
toString方法。
PartMap的默认接受类型是
Map<String,RequestBody>,
非
RequestBody类型会通过
Conventer转换
①. FormUrlEncoded
表示请求体是一个
Form表单,你在网站上看到的登录页面就是用这种请求方式
Content-Type:application/x-www-form-urlencoded
/** * {@link FormUrlEncoded} 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded) * <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值 */ @POST("/form") @FormUrlEncoded Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age); // 演示 @FormUrlEncoded 和 @Field Call<ResponseBody> call1 = service.testFormUrlEncoded1("怪盗kidou", 24); =============================================================================================== /** * Map的key作为表单的键 */ @POST("/form") @FormUrlEncoded Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map); // 演示 @FormUrlEncoded 和 @FieldMap // 实现的效果与上面想同 Map<String, Object> map = new HashMap<>(); map.put("username", "怪盗kidou"); map.put("age", 24); Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);
②. Multipart
表示请求体是一个支持文件上传的
Form表单,你看到 带文件上传的网页 就是用这种方式
Content-Type:multipart/form-data
/** * {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型 * 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息), */ @POST("/form") @Multipart Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file); // 演示 @Multipart 和 @Part MediaType textType = MediaType.parse("text/plain"); RequestBody name = RequestBody.create(textType, "怪盗kidou"); RequestBody age = RequestBody.create(textType, "25"); File file = new File(filename); RequestBody requestFile = RequestBody.create(MediaType.parse("application/octet-stream"), file); MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), requestFile); Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart); =============================================================================================== /** * PartMap 注解支持一个Map作为参数,支持 {@link RequestBody } 类型, * 如果有其它的类型,会被{@link retrofit2.Converter}转换,如后面会介绍的 使用{@link com.google.gson.Gson} 的 {@link retrofit2.converter.gson.GsonRequestBodyConverter} * 所以{@link MultipartBody.Part} 就不适用了,所以文件只能用<b> @Part MultipartBody.Part </b> */ @POST("/form") @Multipart Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file); // 演示 @Multipart 和 @PartMap // 实现和上面同样的效果 Map<String, RequestBody> fileUpload2Args = new HashMap<>(); fileUpload2Args.put("name", name); fileUpload2Args.put("age", age); //这里并不会被当成文件,因为没有文件名(包含在Content-Disposition请求头中),但上面的 filePart 有 //fileUpload2Args.put("file", file); Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); //单独处理文件
(4)用于URL –> Path
、Query
、QueryMap
、 Url
①. Pathpublic interface BlogService { @GET("blog/{id}") //这里的{id} 表示是一个变量 Call<ResponseBody> getFirstBlog(/** 这里的id表示的是上面的{id} */@Path("id") int id); }
②. Query
public interface BlogService { /** * 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供 * 对于Query和QueryMap,如果不是String(或Map的第个泛型参数不是String)时 * 会被调用toString * Url支持的类型有 okhttp3.HttpUrl, String, java.net.URI, android.net.Uri * {@link retrofit2.http.QueryMap} 用法和{@link retrofit2.http.FieldMap} 用法一样,不再说明 */ @GET //当有URL注解时,这里的URL就省略了 Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll); }
③. QueryMap
基本使用
public interface NewsService() { @GET("/news") Call<ResponseBody> getNews(@QueryMap Map<String, String> options); } Map<String, String> data = new HashMap<>(); data.put("author", "Marcus"); data.put("page", String.valueOf(2)); // simplified call to request the news with already initialized service Call<List<News>> call = newsService.getNews(data); call.enqueue(…); 结果:http://your.api.url/news?page=2&author=Marcus
QueryMap Options : encoded
QueryMap有个对于编码的
option field:
encoded:
true或者
false,默认为
false
Call<List<News>> getNews((@QueryMap(encoded=true) Map<String, String> options);
在请求的
URL之前启用编码将编码单个字符(即
encoded=true),比如使用
key为
author,
value为
marcus-poehls,最后会被转为
author=marcus%2Dpoehls。
encoded=false为
author=marcus-poehls
④. Url
使用
@Url可以动态设置
Urls
Retrofit 2 使用 OkHttp的 HttpUrl 来解析每个 Url的端点 就像 网站上的
<a href="">…</a>
/** *Url支持的类型有 okhttp3.HttpUrl, String, java.net.URI, android.net.Uri */ public interface UserService { @GET public Call<ResponseBody> profilePicture(@Url String url); }
Retrofit retrofit = Retrofit.Builder() .baseUrl("https://your.api.url/"); .build(); UserService service = retrofit.create(UserService.class); service.profilePicture("https://s3.amazon.com/profile-picture/path"); // 结果为: // https://s3.amazon.com/profile-picture/path[/code]Retrofit retrofit = Retrofit.Builder() .baseUrl("https://your.api.url/v2/"); .build(); UserService service = retrofit.create(UserService.class); service.profilePicture("/profile-picture/path"); // 结果为: // https://your.api.url/profile-picture/path[/code](5)小注意–> 补充
①.Query、Field和Part这三者都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组。Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids); //结果:ids[]=0&ids[]=1&ids[]=2
②. 如何在OkHttp Interceptor 中管理 Headers
.header(key, val): will override preexisting headers identified by key 会重写
.addHeader(key, val): will add the header and don’t override preexisting ones 不会重写okHttpClient.interceptors().add(new Interceptor() { @Override public Response intercept(Interceptor.Chain chain) throws IOException { Request original = chain.request(); // Request customization: add request headers Request.Builder requestBuilder = original.newBuilder() .header("Authorization", "auth-value"); // <-- this is the important line Request request = requestBuilder.build(); return chain.proceed(request); } });okHttpClient.interceptors().add(new Interceptor() { @Override public Response intercept(Interceptor.Chain chain) throws IOException { Request original = chain.request(); // Request customization: add request headers Request.Builder requestBuilder = original.newBuilder() .addHeader("Cache-Control", "no-cache") .addHeader("Cache-Control", "no-store"); Request request = requestBuilder.build(); return chain.proceed(request); } });
③. 如何添加Query参数 给每个请求OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); httpClient.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); HttpUrl originalHttpUrl = original.url(); HttpUrl url = originalHttpUrl.newBuilder() .addQueryParameter("apikey", "your-actual-api-key") .build(); // Request customization: add request headers Request.Builder requestBuilder = original.newBuilder() .url(url); Request request = requestBuilder.build(); return chain.proceed(request); } });
④. Url拼接规范# Example 1 base url: https://futurestud.io/api/v3/ endpoint: my/endpoint Result: https://futurestud.io/api/v3/my/endpoint # Example 2 base url: https://futurestud.io/api/v3/ endpoint: /api/v2/another/endpoint Result: https://futurestud.io/api/v2/another/endpoint # Example 3 — completely different url base url: http://futurestud.io/api/ endpoint: https://api.futurestud.io/ Result: https://api.futurestud.io/ # Example 4 — Keep the base url’s scheme base url: https://futurestud.io/api/ endpoint: //api.futurestud.io/ Result: https://api.futurestud.io/ # Example 5 — Keep the base url’s scheme base url: http://futurestud.io/api/ endpoint: //api.github.com Result: http://api.github.com
参考整理
https://futurestud.io/blog/retrofit-send-objects-in-request-body
http://www.jianshu.com/p/308f3c54abdd
相关文章推荐
- jQuery的选择器
- source for Android API 19 Platform not found (Android Studio 2.0)
- Toolbar
- 彻底理解PHP的SESSION机制
- javascript正则表达式的基本知识
- 计数排序题
- ubuntu配置openc环境
- mysql根据汉字首字母排序的方法
- 模板容器类的实现一(基于动态数组)
- C# 使用OCR识别中文
- Java中的二分法查找算法
- DAY02_不要自己随便去‘创造’加密算法
- nodejs的后端字符串验证器-validator
- gdb optimized out错误解决
- 蘑菇街 修理桌子
- Java 序列化Serializable详解
- 自学jQuery的随笔1
- POJ 3468 A Simple Problem with Integers(区间更新)
- poj3278 线上的BFS搜索
- EBS FA资产模块新增CIP在建工程手册