精华内容
下载资源
问答
  • Retrofit

    2016-01-31 14:48:03
    Retrofit
    Retrofit
    
    展开全文
  • retrofit

    2016-11-20 23:05:54
    1、Retrofit介绍...在上篇博客中介绍retrofit入门官方介绍地址 http://square.github.io/retrofit/2、流程示意图3、Retrofit代码分析API类中public interface Api { //http://gank.

    1、Retrofit介绍

    http://blog.csdn.net/androidxiaogang/article/details/51725798
    在上篇博客中介绍retrofit入门

    官方介绍地址
    http://square.github.io/retrofit/

    2、流程示意图

    这里写图片描述
    1、Retrofit.build()创建Retrofit对象

    1. OkhttpClient 创建OkhttpClient对象,封装的Okhttp请求
    2. CallBackExecutor 创建CallBackExecutor对象,主要用于请求后的回调
    3. CallAdapter 创建CallAdapter 对象(适用于Rxjava,Java8,Retrofit,通过泛型识别出adapter)
    4. Convertor(GsonConverterFactory.create(),返回类型的转换,比如是json,xml,byte流等之类的)

    2、ServiceMethod.build()

    1. createCallAdapter()
    2. createConvertor
    3. parseAnnoatation 解析请求参数的注解

    3、ExecutorCallBackCall 执行请求
    ExecutorCallBackCall<>backCall callAdapter.adapt(OkHttpCall<>)

    ExecutorCallBackCall.enqueue(new CallBack))
    okHttpCall.enqueue(new CallBack){
    handler.post(new Runnable){

    callback.onResponse

    })

    requestConvertor

    okhttp3.Request=okHttpCall.toRequest()

    responseConvertor

    Retrofit.Response=okHttpCall.toResponse();
    }

    3、Retrofit代码分析

    API类中

    public interface Api {
        //http://gank.io/api/data/福利/5/1
        @GET("api/data/福利/{pageCount}/{pageIndex}")
        Call<DataInfo> getData(@Path("pageCount") int pageCount,
                     @Path("pageIndex") int pageIndex);
    }

    请求示例

      private void initData(int pages) {
            //使用retrofit配置api
            Retrofit retrofit=new Retrofit.Builder()
                    .baseUrl("http://gank.io/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            Api api =retrofit.create(Api.class);
            Call<DataInfo> call=api.getData(5,pages);
            call.enqueue(new Callback<DataInfo>() {
                @Override
                public void onResponse(Call<DataInfo> call, Response<DataInfo> response) {
    
                    arrayList.addAll(response.body().results);
                    adapter.notifyDataSetChanged();
                    refreshLayout.setRefreshing(false);
    
                }
    
                @Override
                public void onFailure(Call<DataInfo> call, Throwable t) {
                    refreshLayout.setRefreshing(false);
                }
            });
        }
    

    1、构造器模式创建Retrofit对象

    Retrofit retrofit=new Retrofit.Builder()
                    .baseUrl("http://gank.io/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

    源码分析

     public Retrofit build() {
          if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
          }
    //通过工厂模式创建okhttpclient对象
          okhttp3.Call.Factory callFactory = this.callFactory;
          if (callFactory == null) {
          //如果为空的话,就创建
            callFactory = new OkHttpClient();
          }
           //Executor对象,执行后返回,在主线程中(相当于handler)
          Executor callbackExecutor = this.callbackExecutor;
          if (callbackExecutor == null) {
            callbackExecutor = platform.defaultCallbackExecutor();
          }
          //把adapter添加到Call adapter中
          // Make a defensive copy of the adapters and add the default Call adapter.
          List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
          adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
          // Make a defensive copy of the converters.
          List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
    
          return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
              callbackExecutor, validateEagerly);
        }
      }

    然后走了callbackExecutor,在这callbackExecutor中异步请求,然后

    //发起enqueue异步请求,然后通过callbackExecutor中Runnable把请求的结果回调到主线程中
    @Override public void enqueue(final Callback<T> callback) {
          if (callback == null) throw new NullPointerException("callback == null");
    
          delegate.enqueue(new Callback<T>() {
          //callbackExecutor请求返回结果的回调
            @Override public void onResponse(final Call<T> call, final Response<T> response) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  if (delegate.isCanceled()) {
                    // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                    callback.onFailure(call, new IOException("Canceled"));
                  } else {
                    callback.onResponse(call, response);
                  }
                }
              });
            }
            //callbackExecutor失败的回调
            @Override public void onFailure(final Call<T> call, final Throwable t) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  callback.onFailure(call, t);
                }
              });
            }
          });
        }

    2、请求参数的封装

    通过上面得到retrofit对象后,然后进行请求参数封装

     Api api =retrofit.create(Api.class);

    源码分析

    public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          eagerlyValidateMethods(service);
        }
        return (T) 
        //通过动态代理,动态的创建我们传入的接口
        Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              private final Platform platform = Platform.get();
    
              @Override public Object invoke(Object proxy, Method method, Object... args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                //把http 请求接口中的参数,转换成真正的http call请求
                ServiceMethod serviceMethod = loadServiceMethod(method);
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
    

    然后serviceMethod,通过注解配置http请求

    3、通过注解配置http请求(路径,参数)

    请求示例代码

    public interface Api {
        //http://gank.io/api/data/福利/5/1
        @GET("api/data/福利/{pageCount}/{pageIndex}")
        Call<DataInfo> getData(@Path("pageCount") int pageCount,
                     @Path("pageIndex") int pageIndex);
    }

    通过上面的serviceMethod,把我们配置的所有http请求,封装到http call中

    4、发送请求

    在主线程中的方法回调,

     call.enqueue(new Callback<DataInfo>() {
                @Override
                public void onResponse(Call<DataInfo> call, Response<DataInfo> response) {
    
                    arrayList.addAll(response.body().results);
                    adapter.notifyDataSetChanged();
                    refreshLayout.setRefreshing(false);
    
                }
    
                @Override
                public void onFailure(Call<DataInfo> call, Throwable t) {
                    refreshLayout.setRefreshing(false);
                }
            });

    源码分析

    public interface Call<T> extends Cloneable {
      /**
       * 同步的发送请求和相应
       *
       */
      Response<T> execute() throws IOException;
    
      /**
       *异步的发送请求和相应
       * 
       */
      void enqueue(Callback<T> callback);
    
      /**
       * 如果已经发送请求了,返回true
       */
      boolean isExecuted();
    
      /**
       * 取消请求
       */
      void cancel();
    
      /*
       * 请求是否取消
       */
      boolean isCanceled();
    
      /**
       *创建一个新的请求call,即使它已经存在了
       */
      Call<T> clone();
    
      /** 发送原始的http请求 */
      Request request();
    }

    4、retrofit中的设计思想

    1、构建器模式

    将复杂的对象构造它的部件创建相分离,android中很多都使用构建器模式,比如dialog弹窗

    public interface Builder {  
        void buildCarWheel() ;  
        void buildSteeringWheel() ;  
        void buildEngine() ;  
        void buildCarFrame() ;  
        Car getCar() ;  
    }  

    2、工厂模式

    工厂模式就相当于创建实例对象的new
    在retrofit中创建,在这工厂的作用只是创建okhttpclient(可能拓展到httpurlconnection,httpclient)

     okhttp3.Call.Factory callFactory = this.callFactory;
          if (callFactory == null) {
            callFactory = new OkHttpClient();
          }
    

    3、代理模式

    静态代理:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
    动态代理:在程序运行时,运用反射动态创建

    在retrofit中运用动态代理的方式,来创建请求的类(类中的是普通retrofit请求,或者rxjava请求)
    另外(动态代理是AOP,面向切面编程的核心,事实上spring的Aop也是建立在java的动态代理机制之上的。要理解spring,就要先理解java的动态代理机制。)

    4、适配器模式

    要将所有的适配给CallAdapter就需要不同的适配器(接头)
    (RxJavaCallAapter,Java8Adapter,AndroidCallAdapter)

    参考文章
    http://www.jianshu.com/p/45cb536be2f4

    展开全文
  • 前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下... 如果对Retrofit v2.0的源码感兴趣,可看文章:Android:手把手带你深入剖析 Retrofit 2.0 源码 目录![目录](http://upload-

    前言

    • Andrroid开发中,网络请求十分常用
    • 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库

    Github截图

    • 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会喜欢。

    如果对Retrofit v2.0的源码感兴趣,可看文章:Android:手把手带你深入剖析 Retrofit 2.0 源码


    目录

    目录


    1. 简介

    Retrofit简介

    特别注意:

    • 准确来说,Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。
    • 原因:网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装

    本质过程

    • App应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作
    • 在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit根据用户的需求对结果进行解析

    2. 与其他开源请求库对比

    除了Retrofit,如今Android中主流的网络请求框架有:

    • Android-Async-Http
    • Volley
    • OkHttp

    下面是简单介绍:

    网络请求加载 - 介绍

    一图让你了解全部的网络请求库和他们之间的区别!

    网络请求库 - 对比


    附:各个主流网络请求库的Github地址


    3. 使用介绍

    使用 Retrofit 的步骤共有7个:

    步骤1:添加Retrofit库的依赖
    步骤2:创建 接收服务器返回数据 的类
    步骤3:创建 用于描述网络请求 的接口
    步骤4:创建 Retrofit 实例
    步骤5:创建 网络请求接口实例 并 配置网络请求参数
    步骤6:发送网络请求(异步 / 同步)

    封装了 数据转换、线程切换的操作

    步骤7:处理服务器返回的数据

    接下来,我们一步步进行讲解。

    步骤1:添加Retrofit库的依赖

    1. 在 Gradle加入Retrofit库的依赖

    由于Retrofit是基于OkHttp,所以还需要添加OkHttp库依赖

    build.gradle

    dependencies {
        compile 'com.squareup.retrofit2:retrofit:2.0.2'
        // Retrofit库
        compile 'com.squareup.okhttp3:okhttp:3.1.2'
        // Okhttp库
      }
    

    2. 添加 网络权限
    AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET"/>
    

    步骤2:创建 接收服务器返回数据 的类

    Reception.java

    public class Reception {
        ...
        // 根据返回数据的格式和数据解析方式(Json、XML等)定义
        // 下面会在实例进行说明
            }
    

    步骤3:创建 用于描述网络请求 的接口

    • Retrofit将 Http请求 抽象成 Java接口:采用 注解 描述网络请求参数 和配置网络请求参数
    1. 用 动态代理 动态 将该接口的注解“翻译”成一个 Http 请求,最后再执行 Http 请求
    2. 注:接口中的每个方法的参数都需要使用注解标注,否则会报错

    GetRequest_Interface.interface

    public interface GetRequest_Interface {
    
        @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
        Call<Translation>  getCall();
        // @GET注解的作用:采用Get方法发送网络请求
     
        // getCall() = 接收网络请求数据的方法
        // 其中返回类型为Call<*>,*是接收数据的类(即上面定义的Translation类)
        // 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
    }
    

    下面详细介绍Retrofit 网络请求接口 的注解类型。

    注解类型

    注解类型

    注解说明

    第一类:网络请求方法

    网络请求方法注解

    详细说明:
    a. @GET、@POST、@PUT、@DELETE、@HEAD
    以上方法分别对应 HTTP中的网络请求方式

    public interface GetRequest_Interface {
    
        @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
        Call<Translation>  getCall();
        // @GET注解的作用:采用Get方法发送网络请求
        // getCall() = 接收网络请求数据的方法
        // 其中返回类型为Call<*>,*是接收数据的类(即上面定义的Translation类)
    }
    

    此处特意说明URL的组成:Retrofit把 网络请求的URL 分成了两部分设置:

    // 第1部分:在网络请求接口的注解设置
     @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
    Call<Translation>  getCall();
    
    // 第2部分:在创建Retrofit实例时通过.baseUrl()设置
    Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://fanyi.youdao.com/") //设置网络请求的Url地址
                    .addConverterFactory(GsonConverterFactory.create()) //设置数据解析器
                    .build();
    
    // 从上面看出:一个请求的URL可以通过 替换块 和 请求方法的参数 来进行动态的URL更新。
    // 替换块是由 被{}包裹起来的字符串构成
    // 即:Retrofit支持动态改变网络请求根目录
    
    • 网络请求的完整 Url =在创建Retrofit实例时通过.baseUrl()设置 +网络请求接口的注解设置(下面称 “path“ )
    • 具体整合的规则如下:

    URL整合规则

    建议采用第三种方式来配置,并尽量使用同一种路径形式。

    b. @HTTP

    • 作用:替换**@GET、@POST、@PUT、@DELETE、@HEAD**注解的作用 及 更多功能拓展
    • 具体使用:通过属性method、path、hasBody进行设置
    public interface GetRequest_Interface {
        /**
         * method:网络请求的方法(区分大小写)
         * path:网络请求地址路径
         * hasBody:是否有请求体
         */
        @HTTP(method = "GET", path = "blog/{id}", hasBody = false)
        Call<ResponseBody> getCall(@Path("id") int id);
        // {id} 表示是一个变量
        // method 的值 retrofit 不会做处理,所以要自行保证准确
    }
    

    第二类:标记

    标记类注解

    a. @FormUrlEncoded

    • 作用:表示发送form-encoded的数据

    每个键值对需要用@Filed来注解键名,随后的对象需要提供值。

    b. @Multipart

    • 作用:表示发送form-encoded的数据(适用于 有文件 上传的场景)

    每个键值对需要用@Part来注解键名,随后的对象需要提供值。

    具体使用如下:
    GetRequest_Interface

    public interface GetRequest_Interface {
            /**
             *表明是一个表单格式的请求(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);
             
            /**
             * {@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);
    
    }
    
    // 具体使用
           GetRequest_Interface service = retrofit.create(GetRequest_Interface.class);
            // @FormUrlEncoded 
            Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
            
            //  @Multipart
            RequestBody name = RequestBody.create(textType, "Carson");
            RequestBody age = RequestBody.create(textType, "24");
    
            MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
            Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
    

    第三类:网络请求参数

    网络请求参数注解

    详细说明

    a. @Header & @Headers

    • 作用:添加请求头 &添加不固定的请求头
    • 具体使用如下:
    // @Header
    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    
    // @Headers
    @Headers("Authorization: authorization")
    @GET("user")
    Call<User> getUser()
    
    // 以上的效果是一致的。
    // 区别在于使用场景和使用方式
    // 1. 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
    // 2. 使用方式:@Header作用于方法的参数;@Headers作用于方法
    

    b. @Body

    • 作用:以 Post方式 传递 自定义数据类型 给服务器
    • 特别注意:如果提交的是一个Map,那么作用相当于 @Field

    不过Map要经过 FormBody.Builder 类处理成为符合 Okhttp 格式的表单,如:

    FormBody.Builder builder = new FormBody.Builder();
    builder.add("key","value");
    
    

    c. @Field & @FieldMap

    • 作用:发送 Post请求 时提交请求的表单字段
    • 具体使用:与 @FormUrlEncoded 注解配合使用
    public interface GetRequest_Interface {
            /**
             *表明是一个表单格式的请求(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);
    
    /**
             * Map的key作为表单的键
             */
            @POST("/form")
            @FormUrlEncoded
            Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
    
    }
    
    // 具体使用
             // @Field
            Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
    
            // @FieldMap
            // 实现的效果与上面相同,但要传入Map
            Map<String, Object> map = new HashMap<>();
            map.put("username", "Carson");
            map.put("age", 24);
            Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);
    

    d. @Part & @PartMap

    • 作用:发送 Post请求 时提交请求的表单字段

    与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景

    • 具体使用:与 @Multipart 注解配合使用
    public interface GetRequest_Interface {
    
              /**
             * {@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);
    
            /**
             * 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);
    
            @POST("/form")
            @Multipart
            Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody> args);
    }
    
    // 具体使用
     MediaType textType = MediaType.parse("text/plain");
            RequestBody name = RequestBody.create(textType, "Carson");
            RequestBody age = RequestBody.create(textType, "24");
            RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");
    
            // @Part
            MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
            Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
            ResponseBodyPrinter.printResponseBody(call3);
    
            // @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); //单独处理文件
            ResponseBodyPrinter.printResponseBody(call4);
    }
    
    

    e. @Query和@QueryMap

    • 作用:用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)

    如:url = http://www.println.net/?cate=android,其中,Query = cate

    • 具体使用:配置时只需要在接口方法中增加一个参数即可:
       @GET("/")    
       Call<String> cate(@Query("cate") String cate);
    }
    
    // 其使用方式同 @Field与@FieldMap,这里不作过多描述
    
    

    f. @Path

    • 作用:URL地址的缺省值
    • 具体使用:
    public interface GetRequest_Interface {
    
            @GET("users/{user}/repos")
            Call<ResponseBody>  getBlog(@Path("user") String user );
            // 访问的API是:https://api.github.com/users/{user}/repos
            // 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
        }
    

    g. @Url

    • 作用:直接传入一个请求的 URL变量 用于URL设置
    • 具体使用:
    public interface GetRequest_Interface {
    
            @GET
            Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
           // 当有URL注解时,@GET传入的URL就可以省略
           // 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
    
    }
    

    汇总

    汇总

    步骤4:创建 Retrofit 实例

     Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://fanyi.youdao.com/") // 设置网络请求的Url地址
                    .addConverterFactory(GsonConverterFactory.create()) // 设置数据解析器
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava平台
                    .build();
    

    a. 关于数据解析器(Converter)

    • Retrofit支持多种数据解析方式
    • 使用时需要在Gradle添加依赖
    数据解析器 Gradle依赖
    Gson com.squareup.retrofit2:converter-gson:2.0.2
    Jackson com.squareup.retrofit2:converter-jackson:2.0.2
    Simple XML com.squareup.retrofit2:converter-simplexml:2.0.2
    Protobuf com.squareup.retrofit2:converter-protobuf:2.0.2
    Moshi com.squareup.retrofit2:converter-moshi:2.0.2
    Wire com.squareup.retrofit2:converter-wire:2.0.2
    Scalars com.squareup.retrofit2:converter-scalars:2.0.2

    b. 关于网络请求适配器(CallAdapter)

    • Retrofit支持多种网络请求适配器方式:guava、Java8和rxjava

    使用时如使用的是 Android 默认的 CallAdapter,则不需要添加网络请求适配器的依赖,否则则需要按照需求进行添加
    Retrofit 提供的 CallAdapter

    • 使用时需要在Gradle添加依赖:
    网络请求适配器 Gradle依赖
    guava com.squareup.retrofit2:adapter-guava:2.0.2
    Java8 com.squareup.retrofit2:adapter-java8:2.0.2
    rxjava com.squareup.retrofit2:adapter-rxjava:2.0.2

    步骤5:创建 网络请求接口实例

            // 创建 网络请求接口 的实例
            GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
    
            //对 发送请求 进行封装
            Call<Reception> call = request.getCall();
    

    步骤6:发送网络请求(异步 / 同步)

    封装了 数据转换、线程切换的操作

    //发送网络请求(异步)
            call.enqueue(new Callback<Translation>() {
                //请求成功时回调
                @Override
                public void onResponse(Call<Translation> call, Response<Translation> response) {
                    //请求处理,输出结果
                    response.body().show();
                }
    
                //请求失败时候的回调
                @Override
                public void onFailure(Call<Translation> call, Throwable throwable) {
                    System.out.println("连接失败");
                }
            });
    
    // 发送网络请求(同步)
    Response<Reception> response = call.execute();
    
    

    步骤7:处理返回数据

    通过response类的 body()对返回的数据进行处理

    //发送网络请求(异步)
            call.enqueue(new Callback<Translation>() {
                //请求成功时回调
                @Override
                public void onResponse(Call<Translation> call, Response<Translation> response) {
                    // 对返回数据进行处理
                    response.body().show();
                }
    
                //请求失败时候的回调
                @Override
                public void onFailure(Call<Translation> call, Throwable throwable) {
                    System.out.println("连接失败");
                }
            });
    
    // 发送网络请求(同步)
      Response<Reception> response = call.execute();
      // 对返回数据进行处理
      response.body().show();
    
    

    4. 实例讲解

    接下来,我将用两个实例分别对 Retrofit GET方式 和 POST方式进行 网络请求 讲解。

    4.1 实例1

    • 实现功能:将中文翻译成英文
    • 实现方案:采用Get方法对 金山词霸API 发送网络请求

    采用 Gson 进行数据解析

    金山词典

    • 步骤说明

    步骤1:添加Retrofit库的依赖
    步骤2:创建 接收服务器返回数据 的类
    步骤3:创建 用于描述网络请求 的接口
    步骤4:创建 Retrofit 实例
    步骤5:创建 网络请求接口实例 并 配置网络请求参数
    步骤6:发送网络请求(采用最常用的异步方式)

    封装了 数据转换、线程切换的操作

    步骤7:处理服务器返回的数据

    接下来,我们一步步进行讲解。

    • 具体使用

    步骤1:添加Retrofit库的依赖

    1. 在 Gradle加入Retrofit库的依赖

    由于Retrofit是基于OkHttp,所以还需要添加OkHttp库依赖

    build.gradle

    dependencies {
        compile 'com.squareup.retrofit2:retrofit:2.0.2'
        // Retrofit库
        compile 'com.squareup.okhttp3:okhttp:3.1.2'
        // Okhttp库
      }
    

    2. 添加 网络权限
    AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET"/>
    

    步骤2:创建 接收服务器返回数据 的类

    • 金山词霸API 的数据格式说明如下:
    // URL模板
    http://fy.iciba.com/ajax.php
    
    // URL实例
    http://fy.iciba.com/ajax.php?a=fy&f=auto&t=auto&w=hello%20world
    
    // 参数说明:
    // a:固定值 fy
    // f:原文内容类型,日语取 ja,中文取 zh,英语取 en,韩语取 ko,德语取 de,西班牙语取 es,法语取 fr,自动则取 auto
    // t:译文内容类型,日语取 ja,中文取 zh,英语取 en,韩语取 ko,德语取 de,西班牙语取 es,法语取 fr,自动则取 auto
    // w:查询内容
    

    API格式说明

    • 根据 金山词霸API 的数据格式,创建 接收服务器返回数据 的类:

    Translation.java

    public class Translation {
            private int status;
    
        private content content;
        private static class content {
            private String from;
            private String to;
            private String vendor;
            private String out;
            private int errNo;
        }
    
        //定义 输出返回数据 的方法
        public void show() {
            System.out.println(status);
            
            System.out.println(content.from);
            System.out.println(content.to);
            System.out.println(content.vendor);
            System.out.println(content.out);
            System.out.println(content.errNo);
        }
    }
        
    

    步骤3:创建 用于描述网络请求 的接口

    采用 注解 描述 网络请求参数。
    GetRequest_Interface.java

    public interface GetRequest_Interface {
        
     @GET("ajax.php?a=fy&f=auto&t=auto&w=hello%20world")
        Call<Translation> getCall();
        // 注解里传入 网络请求 的部分URL地址
        // Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
        // 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
        // getCall()是接受网络请求数据的方法
    }
    

    接下来的步骤均在GetRequest.java内实现(看注释)

    步骤4:创建Retrofit对象
    步骤5:创建 网络请求接口 的实例
    步骤6:发送网络请求

    以最常用的 异步请求 为例

    步骤7:处理返回数据

    GetRequest.java

    public class GetRequest extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            request();
            // 使用Retrofit封装的方法
        }
        public void request() {
    
            //步骤4:创建Retrofit对象
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://fy.iciba.com/") // 设置 网络请求 Url
                    .addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖)
                    .build();
    
            // 步骤5:创建 网络请求接口 的实例
            GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
    
            //对 发送请求 进行封装
            Call<Translation> call = request.getCall();
    
            //步骤6:发送网络请求(异步)
            call.enqueue(new Callback<Translation>() {
                //请求成功时回调
                @Override
                public void onResponse(Call<Translation> call, Response<Translation> response) {
                    // 步骤7:处理返回的数据结果
                    response.body().show();
                }
    
                //请求失败时回调
                @Override
                public void onFailure(Call<Translation> call, Throwable throwable) {
                    System.out.println("连接失败");
                }
            });
        }
    }
    

    由于此处采用了 Gson 解析,所以需要在 Gradle加入依赖
    build.gradle

    compile 'com.squareup.retrofit2:converter-gson:2.0.2'
    

    运行结果

    运行结果

    Demo地址

    Carson_Ho的Github:https://github.com/Carson-Ho/RetrofitDemo


    4.2 实例2

    • 实现的功能:将 英文 翻译成 中文
    • 实现方法:采用Post方法对 有道API 发送网络请求

    采用 Gson 进行数据解析

    有道翻译

    • 使用步骤

    步骤1:添加Retrofit库的依赖
    步骤2:创建 接收服务器返回数据 的类
    步骤3:创建 用于描述网络请求 的接口
    步骤4:创建 Retrofit 实例
    步骤5:创建 网络请求接口实例 并 配置网络请求参数
    步骤6:发送网络请求(采用最常用的异步方式)

    封装了 数据转换、线程切换的操作

    步骤7:处理服务器返回的数据

    接下来,我们一步步进行Retrofit的使用。

    • 具体使用

    步骤1:添加Retrofit库的依赖

    1. 在 Gradle加入Retrofit库的依赖

    由于Retrofit是基于OkHttp,所以还需要添加OkHttp库依赖

    build.gradle

    dependencies {
        compile 'com.squareup.retrofit2:retrofit:2.0.2'
        // Retrofit库
        compile 'com.squareup.okhttp3:okhttp:3.1.2'
        // Okhttp库
      }
    

    2. 添加 网络权限
    AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET"/>
    

    步骤2:创建 接收服务器返回数据 的类

    • API 的数据格式说明如下:
    // URL
    http://fanyi.youdao.com/translate
    
    // URL实例
    http://fanyi.youdao.com/translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=
    
    
    // 参数说明
    // doctype:json 或 xml
    // jsonversion:如果 doctype 值是 xml,则去除该值,若 doctype 值是 json,该值为空即可
    // xmlVersion:如果 doctype 值是 json,则去除该值,若 doctype 值是 xml,该值为空即可
    // type:语言自动检测时为 null,为 null 时可为空。英译中为 EN2ZH_CN,中译英为 ZH_CN2EN,日译中为 JA2ZH_CN,中译日为 ZH_CN2JA,韩译中为 KR2ZH_CN,中译韩为 ZH_CN2KR,中译法为 ZH_CN2FR,法译中为 FR2ZH_CN
    // keyform:mdict. + 版本号 + .手机平台。可为空
    // model:手机型号。可为空
    // mid:平台版本。可为空
    // imei:???。可为空
    // vendor:应用下载平台。可为空
    // screen:屏幕宽高。可为空
    // ssid:用户名。可为空
    // abtest:???。可为空
    
    // 请求方式说明
    // 请求方式:POST
    // 请求体:i
    // 请求格式:x-www-form-urlencoded
    

    数据格式说明

    • 根据 有道API 的数据格式,创建 接收服务器返回数据 的类:

    Translation.java

    public class Translation1 {
    
        private String type;
        private int errorCode;
        private int elapsedTime;
        private List<List<TranslateResultBean>> translateResult;
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public int getErrorCode() {
            return errorCode;
        }
    
        public void setErrorCode(int errorCode) {
            this.errorCode = errorCode;
        }
    
        public int getElapsedTime() {
            return elapsedTime;
        }
    
        public void setElapsedTime(int elapsedTime) {
            this.elapsedTime = elapsedTime;
        }
    
        public List<List<TranslateResultBean>> getTranslateResult() {
            return translateResult;
        }
    
        public void setTranslateResult(List<List<TranslateResultBean>> translateResult) {
            this.translateResult = translateResult;
        }
    
        public static class TranslateResultBean {
            /**
             * src : merry me
             * tgt : 我快乐
             */
    
            public String src;
            public String tgt;
    
            public String getSrc() {
                return src;
            }
    
            public void setSrc(String src) {
                this.src = src;
            }
    
            public String getTgt() {
                return tgt;
            }
    
            public void setTgt(String tgt) {
                this.tgt = tgt;
            }
        }
    
    }
    

    步骤3:创建 用于描述网络请求 的接口

    采用 注解 描述 网络请求参数。

    PostRequest_Interface.java

    public interface PostRequest_Interface {
    
        @POST("translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=")
        @FormUrlEncoded
        Call<Translation1> getCall(@Field("i") String targetSentence);
        //采用@Post表示Post方法进行请求(传入部分url地址)
        // 采用@FormUrlEncoded注解的原因:API规定采用请求格式x-www-form-urlencoded,即表单形式
        // 需要配合@Field 向服务器提交需要的字段
    }
    

    接下来的步骤均在PostRequest.java内实现(看注释)

    步骤4:创建Retrofit对象
    步骤5:创建 网络请求接口 的实例
    步骤6:发送网络请求

    以最常用的 异步请求 为例

    步骤7:处理返回数据

    PostRequest.java

    public class PostRequest extends AppCompatActivity {
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            request();
        }
        public void request() {
    
            //步骤4:创建Retrofit对象
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://fanyi.youdao.com/") // 设置 网络请求 Url
                    .addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖)
                    .build();
    
            // 步骤5:创建 网络请求接口 的实例
            PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);
    
            //对 发送请求 进行封装(设置需要翻译的内容)
            Call<Translation1> call = request.getCall("I love you");
    
            //步骤6:发送网络请求(异步)
            call.enqueue(new Callback<Translation1>() {
    
                //请求成功时回调
                @Override
                public void onResponse(Call<Translation1> call, Response<Translation1> response) {
                    // 步骤7:处理返回的数据结果:输出翻译的内容
                    System.out.println(response.body().getTranslateResult().get(0).get(0).getTgt());
                }
    
                //请求失败时回调
                @Override
                public void onFailure(Call<Translation1> call, Throwable throwable) {
                    System.out.println("请求失败");
                    System.out.println(throwable.getMessage());
                }
            });
        }
    
    
    }
    

    由于此处采用了 Gson 解析,所以需要在 Gradle 加入依赖
    build.gradle

    compile 'com.squareup.retrofit2:converter-gson:2.0.2'
    

    运行结果

    运行结果

    Demo地址

    Carson_Ho的Github:https://github.com/Carson-Ho/RetrofitDemo


    5. Retrofit 的拓展使用

    • Retrofit的使用场景非常丰富,如支持RxJavaPrototocobuff
    • 具体设置也非常简单 & 方便:
    <-- 主要在创建Retrofit对象中设置 -->
    Retrofit retrofit = new Retrofit.Builder()
      .baseUrl(""http://fanyi.youdao.com/"")
      .addConverterFactory(ProtoConverterFactory.create()) // 支持Prototocobuff解析
      .addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava
      .build();
    

    具体关于 RxJava的使用这里就不展开,请期待下篇关于 Rxjava的文章。


    6. 总结


    帮顶或评论点赞!因为你的鼓励是我写作的最大动力!


    欢迎关注carson_ho的微信公众号

    示意图

    示意图

    展开全文
  • Retrofit用法详解

    万次阅读 多人点赞 2016-08-06 23:11:33
    一、 简介Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求,其源码详见OkHttp Github。 本文全部...

    ###一、 简介

    Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求,其源码详见OkHttp Github

    本文全部是在Retrofit2.0+版本基础上论述,所用例子全部来自豆瓣Api

    首先先来看一个完整Get请求是如何实现:

    1. 创建业务请求接口,具体代码如下:

      public interface BlueService {
          @GET("book/search")
          Call<BookSearchResponse> getSearchBooks(@Query("q") String name, 
          		@Query("tag") String tag, @Query("start") int start, 
          		@Query("count") int count);
      }
      

      这里需要稍作说明,@GET注解就表示get请求,@Query表示请求参数,将会以key=value的方式拼接在url后面

    2. 需要创建一个Retrofit的示例,并完成相应的配置

      Retrofit retrofit = new Retrofit.Builder()
          .baseUrl("https://api.douban.com/v2/")
          .addConverterFactory(GsonConverterFactory.create())
          .build();
      
      BlueService service = retrofit.create(BlueService.class);
      

      这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等,当然还会有很多其他的配置,下文会详细介绍。还有addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值,具体的下文还会做详细介绍。

    3. 调用请求方法,并得到Call实例

      Call<BookSearchResponse> call = mBlueService.getSearchBooks("小王子", "", 0, 3);
      

      Call其实在Retrofit中就是行使网络请求并处理返回值的类,调用的时候会把需要拼接的参数传递进去,此处最后得到的url完整地址为

      https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&tag=&start=0&count=3

    4. 使用Call实例完成同步或异步请求

      • 同步请求

        BookSearchResponse response = call.execute().body();
        

        这里需要注意的是网络请求一定要在子线程中完成,不能直接在UI线程执行,不然会crash

      • 异步请求

        call.enqueue(new Callback<BookSearchResponse>() {
          @Override
          public void onResponse(Call<BookSearchResponse> call, 		Response<BookSearchResponse> response) {
          	asyncText.setText("异步请求结果: " + response.body().books.get(0).altTitle);
          }
          @Override
          public void onFailure(Call<BookSearchResponse> call, Throwable t) {
        
          }
        });
        

    二、如何使用

    首先需要在build.gradle文件中引入需要的第三包,配置如下:

    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    

    引入完第三包接下来就可以使用Retrofit来进行网络请求了。接下来会对不同的请求方式做进一步的说明。

    Get方法

    1. @Query

    Get方法请求参数都会以key=value的方式拼接在url后面,Retrofit提供了两种方式设置请求参数。第一种就是像上文提到的直接在interface中添加@Query注解,还有一种方式是通过Interceptor实现,直接看如何通过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);
        }
    }
    

    addQueryParameter就是添加请求参数的具体代码,这种方式比较适用于所有的请求都需要添加的参数,一般现在的网络请求都会添加token作为用户标识,那么这种方式就比较适合。

    创建完成自定义的Interceptor后,还需要在Retrofit创建client处完成添加

    addInterceptor(new CustomInterceptor())
    
    2. @QueryMap

    如果Query参数比较多,那么可以通过@QueryMap方式将所有的参数集成在一个Map统一传递,还以上文中的get请求方法为例

    public interface BlueService {
        @GET("book/search")
        Call<BookSearchResponse> getSearchBooks(@QueryMap Map<String, String> options);
    }
    

    调用的时候将所有的参数集合在统一的map中即可

    Map<String, String> options = new HashMap<>();
    map.put("q", "小王子");
    map.put("tag", null);
    map.put("start", "0");
    map.put("count", "3");
    Call<BookSearchResponse> call = mBlueService.getSearchBooks(options);
    
    3. Query集合

    假如你需要添加相同Key值,但是value却有多个的情况,一种方式是添加多个@Query参数,还有一种简便的方式是将所有的value放置在列表中,然后在同一个@Query下完成添加,实例代码如下:

    public interface BlueService {
        @GET("book/search")
        Call<BookSearchResponse> getSearchBooks(@Query("q") List<String> name);
    }
    

    最后得到的url地址为

    https://api.douban.com/v2/book/search?q=leadership&q=beyond%20feelings
    
    4. Query非必填

    如果请求参数为非必填,也就是说即使不传该参数,服务端也可以正常解析,那么如何实现呢?其实也很简单,请求方法定义处还是需要完整的Query注解,某次请求如果不需要传该参数的话,只需填充null即可。

    针对文章开头提到的get的请求,加入按以下方式调用

    Call<BookSearchResponse> call = mBlueService.getSearchBooks("小王子", null, 0, 3);
    

    那么得到的url地址为

    https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&start=0&count=3
    
    5. @Path

    如果请求的相对地址也是需要调用方传递,那么可以使用@Path注解,示例代码如下:

    @GET("book/{id}")
    Call<BookResponse> getBook(@Path("id") String id);
    

    业务方想要在地址后面拼接书籍id,那么通过Path注解可以在具体的调用场景中动态传递,具体的调用方式如下:

     Call<BookResponse> call = mBlueService.getBook("1003078");
    

    此时的url地址为

    https://api.douban.com/v2/book/1003078
    

    @Path可以用于任何请求方式,包括Post,Put,Delete等等

    Post请求

    1. @field

    Post请求需要把请求参数放置在请求体中,而非拼接在url后面,先来看一个简单的例子

     @FormUrlEncoded
     @POST("book/reviews")
     Call<String> addReviews(@Field("book") String bookId, @Field("title") String title,
     @Field("content") String content, @Field("rating") String rating);
    

    这里有几点需要说明的

    • @FormUrlEncoded将会自动将请求参数的类型调整为application/x-www-form-urlencoded,假如content传递的参数为Good Luck,那么最后得到的请求体就是

      content=Good+Luck
      

      FormUrlEncoded不能用于Get请求

    • @Field注解将每一个请求参数都存放至请求体中,还可以添加encoded参数,该参数为boolean型,具体的用法为

      @Field(value = "book", encoded = true) String book
      

      encoded参数为true的话,key-value-pair将会被编码,即将中文和特殊字符进行编码转换

    2. @FieldMap

    上述Post请求有4个请求参数,假如说有更多的请求参数,那么通过一个一个的参数传递就显得很麻烦而且容易出错,这个时候就可以用FieldMap

     @FormUrlEncoded
     @POST("book/reviews")
     Call<String> addReviews(@FieldMap Map<String, String> fields);
    
    3. @Body

    如果Post请求参数有多个,那么统一封装到类中应该会更好,这样维护起来会非常方便

    @FormUrlEncoded
    @POST("book/reviews")
    Call<String> addReviews(@Body Reviews reviews);
    
    public class Reviews {
        public String book;
        public String title;
        public String content;
        public String rating;
    }
    

    其他请求方式

    除了Get和Post请求,Http请求还包括Put,Delete等等,用法和Post相似,所以就不再单独介绍了。

    上传

    上传因为需要用到Multipart,所以需要单独拿出来介绍,先看一个具体上传的例子

    首先还是需要新建一个interface用于定义上传方法

    public interface FileUploadService {  
        // 上传单个文件
        @Multipart
        @POST("upload")
        Call<ResponseBody> uploadFile(
                @Part("description") RequestBody description,
                @Part MultipartBody.Part file);
    
        // 上传多个文件
        @Multipart
        @POST("upload")
        Call<ResponseBody> uploadMultipleFiles(
                @Part("description") RequestBody description,
                @Part MultipartBody.Part file1,
                @Part MultipartBody.Part file2);
    }
    

    接下来我们还需要在Activity和Fragment中实现两个工具方法,代码如下:

    public static final String MULTIPART_FORM_DATA = "multipart/form-data";
    
    @NonNull
    private RequestBody createPartFromString(String descriptionString) {  
        return RequestBody.create(
                MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
    }
    
    @NonNull
    private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {  
        File file = FileUtils.getFile(this, fileUri);
    
        // 为file建立RequestBody实例
        RequestBody requestFile =
            RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file);
    
        // MultipartBody.Part借助文件名完成最终的上传
        return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
    }
    

    好了,接下来就是最终的上传文件代码了

    Uri file1Uri = ... // 从文件选择器或者摄像头中获取 
    Uri file2Uri = ... 
    
    // 创建上传的service实例
    FileUploadService service =  
            ServiceGenerator.createService(FileUploadService.class);
    
    // 创建文件的part (photo, video, ...)
    MultipartBody.Part body1 = prepareFilePart("video", file1Uri);  
    MultipartBody.Part body2 = prepareFilePart("thumbnail", file2Uri);
    
    // 添加其他的part
    RequestBody description = createPartFromString("hello, this is description speaking");
    
    // 最后执行异步请求操作
    Call<ResponseBody> call = service.uploadMultipleFiles(description, body1, body2);  
    call.enqueue(new Callback<ResponseBody>() {  
        @Override
        public void onResponse(Call<ResponseBody> call,
                Response<ResponseBody> response) {
            Log.v("Upload", "success");
        }
        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e("Upload error:", t.getMessage());
        }
    });
    

    三、其他必须知道的事项

    1. 添加自定义的header

    Retrofit提供了两个方式定义Http请求头参数:静态方法和动态方法,静态方法不能随不同的请求进行变化,头部信息在初始化的时候就固定了。而动态方法则必须为每个请求都要单独设置。

    • 静态方法

      public interface BlueService {
      	@Headers("Cache-Control: max-age=640000")
          @GET("book/search")
          Call<BookSearchResponse> getSearchBooks(@Query("q") String name, 
          		@Query("tag") String tag, @Query("start") int start, 
          		@Query("count") int count);
      }
      

      当然你想添加多个header参数也是可以的,写法也很简单

      public interface BlueService {
      	@Headers({
              "Accept: application/vnd.yourapi.v1.full+json",
              "User-Agent: Your-App-Name"
          })
          @GET("book/search")
          Call<BookSearchResponse> getSearchBooks(@Query("q") String name, 
          		@Query("tag") String tag, @Query("start") int start, 
          		@Query("count") int count);
      }
      

      此外也可以通过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", "Your-App-Name")
                  .header("Accept", "application/vnd.yourapi.v1.full+json")
                  .method(original.method(), original.body())
                  .build();
              return chain.proceed(request);
          }
      }
      

      添加header参数Request提供了两个方法,一个是header(key, value),另一个是**.addHeader(key, value)**,两者的区别是,header()如果有重名的将会覆盖,而addHeader()允许相同key值的header存在

      然后在OkHttp创建Client实例时,添加RequestInterceptor即可

      private static OkHttpClient getNewClient(){
        return new OkHttpClient.Builder()
          .addInterceptor(new RequestInterceptor())
          .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
          .build();
      }
      
    • 动态方法

      public interface BlueService {
          @GET("book/search")
          Call<BookSearchResponse> getSearchBooks(
          @Header("Content-Range") String contentRange, 
          @Query("q") String name, @Query("tag") String tag, 
          @Query("start") int start, @Query("count") int count);
      }
      

    2. 网络请求日志

    调试网络请求的时候经常需要关注一下请求参数和返回值,以便判断和定位问题出在哪里,Retrofit官方提供了一个很方便查看日志的Interceptor,你可以控制你需要的打印信息类型,使用方法也很简单。

    首先需要在build.gradle文件中引入logging-interceptor

    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
    

    同上文提到的CustomInterceptor和RequestInterceptor一样,添加到OkHttpClient创建处即可,完整的示例代码如下:

    private static OkHttpClient getNewClient(){
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        return new OkHttpClient.Builder()
               .addInterceptor(new CustomInterceptor())
               .addInterceptor(logging)
               .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
               .build();
    }
    

    HttpLoggingInterceptor提供了4中控制打印信息类型的等级,分别是NONE,BASIC,HEADERS,BODY,接下来分别来说一下相应的打印信息类型。

    • NONE

      没有任何日志信息

    • Basic

      打印请求类型,URL,请求体大小,返回值状态以及返回值的大小

      D/HttpLoggingInterceptor$Logger: --> POST /upload HTTP/1.1 (277-byte body)  
      D/HttpLoggingInterceptor$Logger: <-- HTTP/1.1 200 OK (543ms, -1-byte body)  
      
    • Headers

      打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码

      <-- 200 OK https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&start=0&count=3&token=tokenValue (3787ms)
      D/OkHttp: Date: Sat, 06 Aug 2016 14:26:03 GMT
      D/OkHttp: Content-Type: application/json; charset=utf-8
      D/OkHttp: Transfer-Encoding: chunked
      D/OkHttp: Connection: keep-alive
      D/OkHttp: Keep-Alive: timeout=30
      D/OkHttp: Vary: Accept-Encoding
      D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
      D/OkHttp: Pragma: no-cache
      D/OkHttp: Cache-Control: must-revalidate, no-cache, private
      D/OkHttp: Set-Cookie: bid=D6UtQR5N9I4; Expires=Sun, 06-Aug-17 14:26:03 GMT; Domain=.douban.com; Path=/
      D/OkHttp: X-DOUBAN-NEWBID: D6UtQR5N9I4
      D/OkHttp: X-DAE-Node: dis17
      D/OkHttp: X-DAE-App: book
      D/OkHttp: Server: dae
      D/OkHttp: <-- END HTTP
      
    • Body

      打印请求和返回值的头部和body信息

      <-- 200 OK https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&tag=&start=0&count=3&token=tokenValue (3583ms)
      D/OkHttp: Connection: keep-alive
      D/OkHttp: Date: Sat, 06 Aug 2016 14:29:11 GMT
      D/OkHttp: Keep-Alive: timeout=30
      D/OkHttp: Content-Type: application/json; charset=utf-8
      D/OkHttp: Vary: Accept-Encoding
      D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
      D/OkHttp: Transfer-Encoding: chunked
      D/OkHttp: Pragma: no-cache
      D/OkHttp: Connection: keep-alive
      D/OkHttp: Cache-Control: must-revalidate, no-cache, private
      D/OkHttp: Keep-Alive: timeout=30
      D/OkHttp: Set-Cookie: bid=ESnahto1_Os; Expires=Sun, 06-Aug-17 14:29:11 GMT; Domain=.douban.com; Path=/
      D/OkHttp: Vary: Accept-Encoding
      D/OkHttp: X-DOUBAN-NEWBID: ESnahto1_Os
      D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
      D/OkHttp: X-DAE-Node: dis5
      D/OkHttp: Pragma: no-cache
      D/OkHttp: X-DAE-App: book
      D/OkHttp: Cache-Control: must-revalidate, no-cache, private
      D/OkHttp: Server: dae
      D/OkHttp: Set-Cookie: bid=5qefVyUZ3KU; Expires=Sun, 06-Aug-17 14:29:11 GMT; Domain=.douban.com; Path=/
      D/OkHttp: X-DOUBAN-NEWBID: 5qefVyUZ3KU
      D/OkHttp: X-DAE-Node: dis17
      D/OkHttp: X-DAE-App: book
      D/OkHttp: Server: dae
      D/OkHttp: {"count":3,"start":0,"total":778,"books":[{"rating":{"max":10,"numRaters":202900,"average":"9.0","min":0},"subtitle":"","author":["[法] 圣埃克苏佩里"],"pubdate":"2003-8","tags":[{"count":49322,"name":"小王子","title":"小王子"},{"count":41381,"name":"童话","title":"童话"},{"count":19773,"name":"圣埃克苏佩里","title":"圣埃克苏佩里"}
      D/OkHttp: <-- END HTTP (13758-byte body)
      

    3. 为某个请求设置完整的URL

    ​ 假如说你的某一个请求不是以base_url开头该怎么办呢?别着急,办法很简单,看下面这个例子你就懂了

    public interface BlueService {  
        @GET
        public Call<ResponseBody> profilePicture(@Url String url);
    }
    
    Retrofit retrofit = Retrofit.Builder()  
        .baseUrl("https://your.api.url/");
        .build();
    
    BlueService service = retrofit.create(BlueService.class);  
    service.profilePicture("https://s3.amazon.com/profile-picture/path");
    

    ​ 直接用@Url注解的方式传递完整的url地址即可。

    4. 取消请求

    Call提供了cancel方法可以取消请求,前提是该请求还没有执行

    String fileUrl = "http://futurestud.io/test.mp4";  
    Call<ResponseBody> call =  
        downloadService.downloadFileWithDynamicUrlSync(fileUrl);
    call.enqueue(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?");
            }
        }
    });
        }
    
    // 触发某个动作,例如用户点击了取消请求的按钮
    call.cancel();  
    }
    

    四、结语

    关于Retrofit常用的方法基本上已经介绍完了,有些请求由于工作保密性的原因,所以就没有放出来,但是基本的方法和操作都是有的,仿照文中提到的代码就可以实现你想要的功能。参考了国外的一则系列教程和liangfei的一篇文章图解 Retrofit - ServiceMethod,由于本人能力有限,有错误或者表述不准确的地方还望多多留言指正。

    欢迎 Follow 我的 GitHub 账户: https://github.com/duanyytop

    展开全文
  • retrofit封装

    2017-09-17 16:36:42
    retrofit封装retrofit封装retrofit封装retrofit封装retrofit封装retrofit封装retrofit封装retrofit封装retrofit封装
  • Retrofit基本使用

    2021-01-03 19:52:36
    文章目录Retrofit基本使用简介Retrofit注解方法注解标记注解请求头注解参数注解简单使用引入Retrofit依赖及添加权限创建Retrofit实例初步封装定义API接口创建代理对象接口调用 – 简介 Retrofit文档 Retrofit Github...
  • Retrofit使用简介

    千次阅读 2016-08-19 10:38:29
    Retrofit
  • Retrofit2详解

    万次阅读 多人点赞 2018-11-19 10:21:06
    Retrofit框架: 它是Square公司开发的现在非常流行的网络框架 retrofit2.0它依赖于OkHttp,在这里我们也不需要显示的导入okHttp,在retrofit中已经导入okhttp3 性能好,处理快,使用简单,Retrofit 是安卓上最...
  • retrofit
  • Retrofit实现原理

    千次阅读 2020-09-04 17:21:52
    Retrofit的使用是动态代理,方法注解,建造者和适配器等成熟的技术。解决api的耦合 一。动态代理 retrofit会生成一个接口实例。 //用retrofit加工出对应的接口实例对象 INetApiService netApiService= retrofit....
  • Retrofit 2.0

    2019-03-11 16:34:20
    1.简介 ...1:添加Retrofit库的依赖 1. 在Gradle加入Retrofit库的依赖 build.gradle dependencies { compile 'com.squareup.retrofit2:retrofit:2.0.2' // Retrofit库 compile 'com.squar...
  • Retrofit学习

    2019-04-30 12:58:30
    Retrofit 官网:https://square.github.io/retrofit/ github:https://github.com/square/retrofit
  • Retrofit源码学习一:Retrofit介绍 Retrofit源码学习二:代理模式 Retrofit源码学习三:Retrofit源码详解一 Retrofit源码学习四:Retrofit源码详解二 Retrofit源码学习五:Retrofit中同步、异步请求解析 ...
  • Retrofit使用

    千次阅读 2019-06-14 06:41:21
    Retrofit是什么? Retrofit就是一个Http请求库,和其它Http库最大区别在于通过大范围使用注解简化Http请求。目前Retrofit 2.0底层是依赖OkHttp实现的,也就是说Retrofit本质上就是对OkHttp的更进一步封装。那么我们...
  • Retrofit封装

    2019-03-17 15:52:37
    package com.bwei.shopping.utils; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitUtils { //单利 private static RetrofitUtils retrofitUtils...
  • Retrofit缓存

    2019-04-11 17:22:19
    相关文章: 在 Retrofit 和 OkHttp 中使用网络缓存,提高访问效率 Retrofit2+okhttp3缓存设置
  • Retrofit详解

    2018-03-08 17:23:54
    Retrofit2.0的使用教程 Retrofit2.0源码分析指南
  • Retrofit入门

    2018-09-25 20:24:30
    想我一年之前每天都在使用Retrofit,一年之后每天都在使用落后的Volley。 因为平时开发中经常发现demo需要进行网络请求,现场编写一个网络请求的库有点手忙脚乱,所以的话干脆写一篇博客记录下来,以后demo再需要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,673
精华内容 10,669
关键字:

retrofit