retrofit 英文名字是改装的意思,也就是说他是对网络请求的一种改装,他不负责进行网络请求,他是对请求方式的一种封装。真正进行网络请求的是okhttp。
如下全部内容在Android Studio已经导入retrofit为基础。导入方式以下:html
compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
retrofit在构建请求方式以前,须要构建一个接口方法,经过这个接口方法的返回值,来进行网络请求。
下面,来经过一些简单的例子了解GET请求。json
咱们要获取百度页面的HTML。首先构建以下接口:api
public interface HtmlService { @GET("/") Call<String> getHtml(); }
注意,GET注解中的参数,和方法中的参数至少要加一个,不然会报错。因为,咱们只须要请求www.baidu.com
,因此get这里不须要加参数,就是/
而后,咱们经过以下步骤,来进行网络请求。
在咱们须要进行网络请求的类中,经过如下的步骤,进行网络请求:cookie
第一步网络
Retrofit retrofit = new Retrofit.Builder(). addConverterFactory(ScalarsConverterFactory.create()). baseUrl("https://www.baidu.com"). build();
经过以上代码,能够简单的构建一个retrofit对象,addConverterFactory
是对response进行解析,里面添加的参数是表示对response用String解析,而后添加一个基础的URL,后续的参数则是经过上面咱们定制的接口来添加,最后构建一个完整的URL。
第二步app
HtmlService htmlService = retrofit.create(HtmlService.class);
经过动态代理,生成一个接口的对象。异步
第三步ide
Call<String> call = htmlService.getHtml();
经过接口的方法获得调用的对象。post
第四步与第五步
异步方法获得response:ui
call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { showText.append(response.body()); } @Override public void onFailure(Call<String> call, Throwable t) { Toast.makeText(MainActivity.this,t.getMessage(),Toast.LENGTH_SHORT).show(); } });
获得的response,经过response.body()
获得响应报文的body部分。
同步方法获得response:
new Thread(new Runnable() { @Override public void run() { try { final String str = call.execute().body(); handler.post(new Runnable() { @Override public void run() { showText.append(str); } }); } catch (IOException e) { e.printStackTrace(); } } }).start();
经过GET请求GankIO的api获得Json:
首先,咱们也是经过接口,构建一个接口方法:
@GET("content/{number}/{page}") Call<HistoryBean> getHistoryData(@Path("number") String number,@Path("page") String page);
这里,方法里面传入的参数会放到@GET
的注解里面。
而后,从新构建一个retrofit对象:
Retrofit retrofit = new Retrofit.Builder(). addConverterFactory(GsonConverterFactory.create()). baseUrl("http://gank.io/api/history/"). build();
这里面添加的解析器是GsonConverterFactory
,表示对response中body提供对象解析。而后的方法和上面相似:
HtmlService htmlService = retrofit.create(HtmlService.class); call = htmlService.getHistoryData("2", "1"); call.enqueue(new Callback<HistoryBean>() { @Override public void onResponse(Call<HistoryBean> call, Response<HistoryBean> response) { HistoryBean hb = response.body(); if(hb == null) return; showText.append(hb.isError() + ""); for(HistoryBean.ResultsBean rb : hb.getResults()){ showText.append(rb.getTitle() + "/n"); } } @Override public void onFailure(Call<HistoryBean> call, Throwable t) { } });
上面的方法是异步获得的,同步的方法和上面相似,就很少说了。
上面的GET方法是没有查询参数的,下面对一个有查询参数的api,进行GET请求,这里咱们利用豆瓣的搜索图书的API
这个API接受4个搜索参数,具体以下:
参数 | 意义 | 备注 |
---|---|---|
q | 查询关键字 | q与tag必传其一 |
tag | 查询的tag | q与tag必传其一 |
start | 取结果的offset | 默认为0 |
count | 取结果的条数 | 默认为20 |
首先,咱们也是构建一个请求接口的方法:
@GET("book/search") Call<BookBean> queryBookInfo(@Query("q") String name);
在这里面,咱们用到了一个新的注解参数@Query
这个参数表示请求参数会以键值对的形式拼接在URL后面。
这样的方式,有一种局限性,由于要在每一个GET接口方法里面写入键值对信息,若是,有些键值对信息是每一个GET方法都须要的,咱们就会经过一个拦截器的形式,统一在请求的时候加上。步骤以下:
Interceptor
。第一步
public class CustomInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); HttpUrl httpUrl = request.url().newBuilder() .addQueryParameter("token", "tokenValue") .build(); request = request.newBuilder().url(httpUrl).build(); return chain.proceed(request); } }
第二步
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(). addInterceptor(new CustomInterceptor()). connectTimeout(1000, TimeUnit.MILLISECONDS); Retrofit retrofit = new Retrofit.Builder(). client(httpClientBuilder.build()). addConverterFactory(GsonConverterFactory.create()). baseUrl("https://api.douban.com/v2/"). build(); HtmlService htmlService = retrofit.create(HtmlService.class); call = htmlService.queryBookInfo("第一行代码");
后续的异步请求基本一致,就不细说了。
实验三的部分,讲了对一个查询参数和一个共有的查询参数的GET请求构建方法,下面多个查询参数的GET请求,看看是否有简单的方式,由于不想在一个方法里,传入4个以上的参数。
请求的API仍是上边的豆瓣的搜索API,他正好有4个请求参数
下面,看以下构建方式:
@GET("book/search") Call<BookBean> queryBookInfo(@QueryMap Map<String,String> options);
而后,将请求参数经过键值对的形式保存到Map里:
Map<String,String> options = new HashMap<>(); options.put("q","第一行代码"); options.put("start","0"); options.put("count","1"); call = htmlService.queryBookInfo(options);
在上面的状况下,有多种键值对,每一种key对应的value都是惟一的,retrofit也支持相同的key,却有多种value的形式。方式以下:
@GET("book/search") Call<BookBean> queryBookInfo(@Query("key") List<String> value);
而后,将value的集合传入方法中,后续的步骤不变,就很少数。
利用retorfit进行post请求与进行get请求没有太多的区别。主要的区别就在构建接口方法上面,有一些不一样,下面经过一些实验来看一下具体的区别。
下面的POST方法API是我本身写的后台来接受简单的POST,就不放出来了。
首先,也是构建一个接口方法:
@FormUrlEncoded @POST("login") Call<String> doLogin(@Field("name")String name,@Field("password") String password);
第一个注解,表示自动将请求参数的类型调整为application/x-www-form-urlencoded
,若是方法参数的注解用了@Field
就必定要用@FormUrlEncoded
。POST注解里面依旧放的是API,方法参数经过@Field
将请求参数放置在请求体中。
后续建立retrofit对象,建立call对象,发起请求,都是和GET方法同样,就很少说了。
这个只不过构建接口方法的时候,有所区别,其余步骤和多种参数进行GET请求同样。
@FormUrlEncoded @POST("login") Call<String> doLogin(@FieldMap Map<String,String> fields);
将多个请求参数保存到Map中,传入方法中,没什么好说的。
一样构建一个接口方法:
@Multipart @POST("upload") Call<ResponseBody> uploadFile(@Part("description") RequestBody description, @Part MultipartBody.Part file);
这里的POST修饰注解就换成了@Multipart
,在方法参数里面,经过@Part
注解来修饰参数。
咱们说一下具体怎么构建这些方法的参数。
首先,构建一个RequestBody对象的description,构建方式以下:
RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), "这是一个文件");
而后,构建一个MultipartBody.Part
对象的file,不过在构建以前,先要建立一个RequestBody对象,经过这个对象才能建立一个MultipartBody.Part
对象。代码以下:
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"),file); MultipartBody.Part body = MultipartBody.Part.createFormData("file",file.getName(),requestBody);
而后和其余方法同样,利用这些对象,生成call,而后进行网络请求。
call = service.uploadFile(description,body);
固然,retrofit支持多种上传图片的方式,其构建方式以下:
// 上传多个文件 @Multipart @POST("upload") Call<ResponseBody> uploadMultipleFiles( @Part("description") RequestBody description, @Part MultipartBody.Part file1, @Part MultipartBody.Part file2);
以上这些实验就是利用Retrofit实现简单POST 请求。
咱们在进行复杂的网络请求的时候,一般要对请求添加头部,retrofit提供了多种添加方式,能够分为以下两种:
静态添加头部,则每次请求的时候,头部的信息都是固定的,不能够更改的。添加静态头部的方式也有多种,方法以下:
下面,咱们分别说一说每一种。
在接口中添加
仍是以上文写到的接口为例,添加Cache-Control
,User-Agent
请求头部。
@Headers({ "Cache-Control: max-age=640000", "User-Agent: app-name" })
经过拦截器添加
和上面统一处理GET参数的定制器同样,一样实现Interceptor
,代码以下:
public class RequestInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); Request request = original.newBuilder() .header("User-Agent", "app-name") .header("Cache-Control", "max-age=640000") .method(original.method(), original.body()) .build(); return chain.proceed(request); } }
而后,在建立okHttp的客户端时,把拦截器加进去,建立retrofit对象时,指定该客户端便可。
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(). addInterceptor(new CustomInterceptor()). addInterceptor(new RequestInterceptor()). connectTimeout(1000, TimeUnit.MILLISECONDS);
动态添加的好处,就在于不一样的请求会有不一样的请求头部,那么可想而知,其添加的部分就是在接口方法里面。
@GET("{number}/{page}") Call<HistoryBean> getHistoryData(@Header("Content-Range") String contentRange ,@Path("number") String number, @Path("page") String page);
后续的使用都是同样的,没什么好说的。
retrofit提供了对网络请求的过程进行打印的日志的插件,须要单独的导入,其方式以下:
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
经过上面的导入代码,能够看出这个日志插件是okHttp特有的,这也能够证实,retrofit只是封装了请求的方式,真正请求的仍是经过okHttp。那么咱们也能够猜出,其设置的方式必然也是经过拦截器,放进okHttp的客户端里面。具体的代码以下:
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(). addInterceptor(new CustomInterceptor()). addInterceptor(new RequestInterceptor()). addInterceptor(httpLoggingInterceptor). connectTimeout(1000, TimeUnit.MILLISECONDS);
注意上面的代码,在设置请求级别上面设置为body,下面说一下具体的级别以及其内涵:
NONE
: 没有任何日志信息。Basic
: 打印请求类型,URL,请求体大小,返回值状态以及返回值的大小。Headers
: 打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码Body
: 打印请求和返回值的头部和body信息。上面就是简单的retrofit的使用,关于利用retrofit结合其余部分如Rx,okHttp等等,或者利用retrofit实现多种文件上传,下载功能,保存cookie等等功能,能够期待后续的文章。