zoukankan      html  css  js  c++  java
  • 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等等功能,可以期待后续的文章。

  • 相关阅读:
    Using Resource File on DotNet
    C++/CLI VS CSharp
    JIT VS NGen
    [Tip: disable vc intellisense]VS2008 VC Intelisense issue
    UVa 10891 Game of Sum(经典博弈区间DP)
    UVa 10723 Cyborg Genes(LCS变种)
    UVa 607 Scheduling Lectures(简单DP)
    UVa 10401 Injured Queen Problem(简单DP)
    UVa 10313 Pay the Price(类似数字分解DP)
    UVa 10635 Prince and Princess(LCS N*logN)
  • 原文地址:https://www.cnblogs.com/qifengshi/p/6060520.html
Copyright © 2011-2022 走看看