Retrofit2.0起步篇

retrofit 英文名字是改装的意思,也就是说他是对网络请求的一种改装,他不负责进行网络请求,他是对请求方式的一种封装。真正进行网络请求的是okhttp。
博客园地址
以下所有内容在Android Studio已经导入retrofit为基础。导入方式如下:

  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请求

retrofit在构建请求方式之前,需要构建一个接口方法,通过这个接口方法的返回值,来进行网络请求。
下面,来通过一些简单的例子了解GET请求。

实验一:对一个简单的html页面进行GET请求

我们要获取百度页面的HTML。首先构建如下接口:

public interface HtmlService {
    @GET("/")
    Call<String> getHtml();
}

注意,GET注解中的参数,和方法中的参数至少要加一个,否则会报错。由于,我们只需要请求www.baidu.com,所以get这里不需要加参数,就是/
然后,我们通过如下步骤,来进行网络请求。
在我们需要进行网络请求的类中,通过以下的步骤,进行网络请求:

  1. 构建retrofit对象。
  2. 动态代理生成接口的对象。
  3. 通过接口的方法,得到要请求的API的调用。
  4. 通过同步/异步的方式,得到response。
  5. 根据需要,处理response。

第一步

Retrofit retrofit = new Retrofit.Builder().          addConverterFactory(ScalarsConverterFactory.create()).
                baseUrl("https://www.baidu.com").
                build();

通过以上代码,可以简单的构建一个retrofit对象,addConverterFactory是对response进行解析,里面添加的参数是表示对response用String解析,然后添加一个基础的URL,后续的参数则是通过上面我们定制的接口来添加,最后构建一个完整的URL。
第二步

HtmlService htmlService = retrofit.create(HtmlService.class);

通过动态代理,生成一个接口的对象。

第三步

Call<String> call = htmlService.getHtml();

通过接口的方法得到调用的对象。

第四步与第五步
异步方法得到response:

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();

实验二:对一个返回JSON格式的API进行GET请求

通过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请求

上面的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方法都需要的,我们就会通过一个拦截器的形式,统一在请求的时候加上。步骤如下:

  1. 自定义一个拦截器实现Interceptor
  2. 创建retrofit的客户端(上面的代码都是默认的客户端),加上这个拦截器。

第一步

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请求构建方法,下面多个查询参数的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的集合传入方法中,后续的步骤不变,就不多数。

利用Retrofit进行简单的POST请求

利用retorfit进行post请求与进行get请求没有太多的区别。主要的区别就在构建接口方法上面,有一些不同,下面通过一些实验来看一下具体的区别。

实验一:将少数参数放到请求体中进行POST请求

下面的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方法一样,就不多说了。

实验二:将多个参数放到请求体中进行POST请求

这个只不过构建接口方法的时候,有所区别,其他步骤和多种参数进行GET请求一样。

@FormUrlEncoded
@POST("login")
Call<String> doLogin(@FieldMap Map<String,String> fields);

将多个请求参数保存到Map中,传入方法中,没什么好说的。

实验三:利用POST进行文件上传

同样构建一个接口方法:

@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 请求。

添加HEADER

我们在进行复杂的网络请求的时候,通常要对请求添加头部,retrofit提供了多种添加方式,可以分为如下两种:

  1. 静态添加头部
  2. 动态添加头部

静态添加头部

静态添加头部,则每次请求的时候,头部的信息都是固定的,不可以更改的。添加静态头部的方式也有多种,方法如下:

  1. 在接口中添加。
  2. 通过拦截器添加。

下面,我们分别说一说每一种。
在接口中添加
还是以上文写到的接口为例,添加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,下面说一下具体的级别以及其内涵:

  1. NONE : 没有任何日志信息。
  2. Basic : 打印请求类型,URL,请求体大小,返回值状态以及返回值的大小。
  3. Headers : 打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码
  4. Body : 打印请求和返回值的头部和body信息。

总结

上面就是简单的retrofit的使用,关于利用retrofit结合其他部分如Rx,okHttp等等,或者利用retrofit实现多种文件上传,下载功能,保存cookie等等功能,可以期待后续的文章。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容