Retrofit 是SQUARE美国一家移动支付公司最近新发布的在Android平台上 Http 访问的开源项目。官方标语:“A type-safe HTTP client for Android and Java”语意很明显是一款Android 安全类型的http客户端。 这里安全指什么呢?是支持https或是支持本地线程安全呢?而且,Retrofit其内部都是支持lambda语法(链式语法),内部支持okHttp, 并且支持响应式RxJava,当然JDK 1.8 和 android studio工具也支持lambda。带着这些疑问 我开始探究一下。

Retrofit 官方GitHub地址 

http://square.github.io/retrofit/


系列文章推荐:

Android 必须知道的网络请求框架库,你不可错过的框架介绍篇

Android Retrofit 2.0(一)初次见面请多多关照

Android Retrofit 2.0(二)使用教程OkHttp3 + Gson + RxJava

Android Retrofit 2.0(三)从源码分析原理 


添加依赖

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'

com.squareup.retrofit2:converter-gson:2.0.0-beta4 此依赖非必须,只是方便我对http返回的数据进行解析,下面会讲到。


基础使用

private  void getLogin() {    Retrofit retrofit = new Retrofit.Builder()            .baseUrl("http://localhost:8080/")            .addConverterFactory(GsonConverterFactory.create())            .build();    ApiManager apiService = retrofit.create(ApiManager.class);    Call call = apiService.getData("lyk", "1234");   call.enqueue(new Callback() {       @Override       public void onResponse(Call call, Response response) {           if (response.isSuccess()) {               // 请求成功           } else {              //直接操作UI 或弹框提示请求失败           }       }       @Override       public void onFailure(Call call, Throwable t) {           //错误处理代码       }   });}

ApiManager接口

public interface ApiManager { @GET("login/") Call getData(@Query("name") String name, @Query("password") String pw);

以上就是实现一个登录Login接口的小功能 ,先了解一下Retrofit的基本用法。


基础介绍


1、首先,实例化一个Retrofit对象。

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

*addConverterFactory 制定数据解析器,上面添加依赖的gson就是用在这里做默认数据返回的, 之后通过build()创建出来。

Retrofit内部自带如下格式:
  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

2、然后,定义带参的请求接口

  @GET("login/")  Call getData(@Query("name") String name, @Query("password") String pw);

Call是继承Cloneable的 并支持泛型,且此类是Retrofit统一返回对象,支持Callback回调,在2.0上已支持RxJava观察者对象Observable,此案例暂时用call ,后面入门了retrofit之后再接入RxJava。

接着我们可以传入制定的解析Modle,就会在主线程里返回对应的model数据,无需开发者手动解析json数据。返回格式由开发者自己设置。

这里主要用注解@get @post 设置请求方式,后面“login/”是方法Url, @Query("name")来设定body的parameters。

  • 如果想用表单 @FieldMap
    @FormUrlEncoded@POST("/url") Call postForm(     @FieldMap Map maps);
  • 如果直接用对象 @Body

    @POST("url") Call PostBody(      @Body Objects objects);
  • 如果直接多参数 @QueryMap

    @PUT("/url")Call queryMap(      @QueryMap Map maps);
  • 如果上传文件 @Part

    @Multipart@POST("/url")Call uploadFlie(      @Part("description") RequestBody description,      @Part("files") MultipartBody.Part file);
  • 如果多文件上传 @PartMap()

    @Multipart@POST("{url}")Call uploadFiles(      @Path("url") String url,      @PartMap() Map maps);

3、最后,调用API发起请求

   Call call = apiService.getData("lyk", "1234");   call.enqueue(new Callback() {       @Override       public void onResponse(Call call, Response response) {       }       @Override       public void onFailure(Call call, Throwable t) {       }   });}

Retrofit支持异步和同步:

  • call.enqueue(new Callback)采用异步请求;
  • call.execute() 采用同步方式。

4、取消请求

 call.cancel();

进阶练习


我们依然要面对的问题是 ,Retrofit 的内部怎么无法输出Log ?header怎么加入?请求怎么支持https?包括怎么结合RxJava? 不用担心,这些问题Retrofit 2.0 都提供了支持okhttp的自定义的Interceptor(拦截器)解决了。通过不同的拦截器可以实现不同的自定义请求形式,比如统一加head、参数、加入证书(ssl)等,前提必须结合okhttp来实现 , 通过给OkHttpClient添加Interceptor,然后给Retrofit设置http即可。Retrofit提供了.client()方法供我们自定义请求,当然默认请求就是okhttps的。

1、开启Log

用拦截器实现, retrofit已经提供了 HttpLoggingInterceptor 里面有四种级别,输出的格式,可以看下面介绍。

public enum Level {    /** No logs. */    NONE,    /**     * Logs request and response lines.     *     * 

Example: *

{@code     * --> POST /greeting http/1.1 (3-byte body)     *     * <-- 200 OK (22ms, 6-byte body)     * }
*/ BASIC, /** * Logs request and response lines and their respective headers. * *

Example: *

{@code     * --> POST /greeting http/1.1     * Host: example.com     * Content-Type: plain/text     * Content-Length: 3     * --> END POST     *     * <-- 200 OK (22ms)     * Content-Type: plain/text     * Content-Length: 6     * <-- END HTTP     * }
*/ HEADERS, /** * Logs request and response lines and their respective headers and bodies (if present). * *

Example: *

{@code     * --> POST /greeting http/1.1     * Host: example.com     * Content-Type: plain/text     * Content-Length: 3     *     * Hi?     * --> END GET     *     * <-- 200 OK (22ms)     * Content-Type: plain/text     * Content-Length: 6     *     * Hello!     * <-- END HTTP     * }
*/ BODY }

例如,开启请求头添加拦截器

   Retrofit retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()                         .addNetworkInterceptor(                                    new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))            .build())

开启body日志添加拦截器

.addNetworkInterceptor(                                    new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))

基础的输出添加拦截器

.addNetworkInterceptor(                                    new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))

2、增加头部信息

通用请求头

 new Retrofit.Builder()           .addConverterFactory(GsonConverterFactory.create())           .client(new OkHttpClient.Builder()                   .addInterceptor(new Interceptor() {                       @Override                       public Response intercept(Chain chain) throws IOException {                           Request request = chain.request()                                   .newBuilder()                                   .addHeader("mac", "f8:00:ea:10:45")                                   .addHeader("uuid", "gdeflatfgfg5454545e")                                   .addHeader("userId", "Fea2405144")                                   .addHeader("netWork", "wifi")                                   .build();                           return chain.proceed(request);                       }                   })                   .build()

特殊API接口单独加入

@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-your-App"})@get("users/{username}")Call   getUser(@Path("username") String username);

3、添加证书Pinning

证书可以在自定义的OkHttpClient加入certificatePinner 实现

OkHttpClient client = new OkHttpClient.Builder()    .certificatePinner(new CertificatePinner.Builder()            .add("YOU API.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")            .add("YOU API..com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")            .add("YOU API..com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")            .add("YOU API..com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")            .build())

4、支持https

加密和普通http客户端请求支持https一样,步骤如下:

  1. CertificateFactory 得到Context.getSocketFactory;
  2. 添加证书源文件;
  3. 绑定到okhttpClient;
  4. 设置okhttpClient到retrofit中。

证书同样可以设置到okhttpclient中,我们可以把证书放到raw路径下:

 SLSocketFactory sslSocketFactory =getSSLSocketFactory_Certificate(context,"BKS", R.raw.XXX);

准备证书源文件

加入证书源文件,我的证书是放在Raw下面的: 


证书

绑定证书

protected static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {if (context == null) {    throw new NullPointerException("context == null");}CertificateFactory certificateFactory;try {    certificateFactory = CertificateFactory.getInstance("X.509");    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());    keyStore.load(null, null);    for (int i = 0; i < certificates.length; i++) {        InputStream certificate = context.getResources().openRawResource(certificates[i]);        keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(certificate));        if (certificate != null) {            certificate.close();        }    }    SSLContext sslContext = SSLContext.getInstance("TLS");    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());    trustManagerFactory.init(keyStore);    sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());   return sslContext.getSocketFactory();

构建HostnameVerifier

 protected static HostnameVerifier getHostnameVerifier(final String[] hostUrls) {    HostnameVerifier TRUSTED_VERIFIER = new HostnameVerifier() {        public boolean verify(String hostname, SSLSession session) {            boolean ret = false;            for (String host : hostUrls) {                if (host.equalsIgnoreCase(hostname)) {                    ret = true;                }            }            return ret;        }    };return TRUSTED_VERIFIER;}

设置setSocketFactory

okhttpBuilder.socketFactory(HttpsFactroy.getSSLSocketFactory(context, certificates));

certificates 是你raw下证书源ID, int[] certificates = {R.raw.myssl}

设置setNameVerifie

okhttpBuilder.hostnameVerifier(HttpsFactroy.getHostnameVerifier(hosts));

hosts是你的host数据,例如 String hosts[]`= {“https//:aaaa,com”, “https//:bbb.com”}

实现自定义 添加到Retrofit

okHttpClient = okhttpBuilder.build();   Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .build();

如果信任所有https请求,
可以直接将OkHttpClient的HostnameVerifier设置为false

OkHttpClient client = new OkHttpClient();    client.setHostnameVerifier(new HostnameVerifier() {        @Override        public boolean verify(String s, SSLSession sslSession) {            return true;        }    });    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {        @Override        public void checkClientTrusted(                java.security.cert.X509Certificate[] x509Certificates,                String s) throws java.security.cert.CertificateException {        }        @Override        public void checkServerTrusted(                java.security.cert.X509Certificate[] x509Certificates,                String s) throws java.security.cert.CertificateException {        }        @Override        public java.security.cert.X509Certificate[] getAcceptedIssuers() {            return new java.security.cert.X509Certificate[] {};        }    } };    try {        SSLContext sc = SSLContext.getInstance("TLS");        sc.init(null, trustAllCerts, new java.security.SecureRandom());        client.setSslSocketFactory(sc.getSocketFactory());    } catch (Exception e) {        e.printStackTrace();    }   clent.protocols(Collections.singletonList(Protocol.HTTP_1_1)) .build();

常规问题

1 url被转义

   http://api.myapi.com/http%3A%2F%2Fapi.mysite.com%2Fuser%2Flist

请将@path改成@url

   public interface APIService {     @GET Call getUsers(@Url String url);}

或者:

  public interface APIService {    @GET("{fullUrl}")    Call getUsers(@Path(value = "fullUrl", encoded = true) String fullUrl);}

2Method方法找不到

java.lang.IllegalArgumentException: Method must not be null

请指定具体请求类型@get @post等

   public interface APIService {    @GET Call getUsers(@Url String url);}

3Url编码不对,@fieldMap parameters must be use FormUrlEncoded

如果用fieldMap加上FormUrlEncoded编码

@POST()@FormUrlEncodedObservable executePost(        @FieldMap Map maps);

上层需要转换将自己的map转换为FieldMap

 @FieldMap(encoded = true) Map<String, Object> parameters,

4 paht和url一起使用

Using @Path and @Url paramers together with retrofit2 

java.lang.IllegalArgumentException: @Path parameters may not be used with @Url. (parameter #4

如果你是这样的:

 @GETCall getOrder(@Url String url, @Path("id") int id);

请在你的url指定占位符.url:

www.myAPi.com/{Id}

看了以上的知识点你发现Retrofit同样支持RxJava,通过以下设置Call适配模式.就可以完美关联RxJava。

 retrofit .addCallAdapterFactory(RxJavaCallAdapterFactory.create())


更多相关文章

  1. Android(安卓)查看SharedPreferences中的数据
  2. Android下创建一个sqlite数据库
  3. Android下Content Provider使用
  4. 【Android(安卓)学习笔记】save data(1)—— SharedPreferences简
  5. android中JSON数据的读写方法
  6. Android使用facebook api(二)
  7. SQLite数据库(2):ANDROID工程中的使用
  8. Android之路之十一(SharedPreferences&SQLite数据库)
  9. 记android学习之路----android中对json数据的解析

随机推荐

  1. android webkit 打开debug调试信息
  2. Android调用系统短信功能发送短信
  3. Android用户界面 UI组件--自动提示输入框
  4. android通过耳机控制音乐播放器
  5. Android的线程使用来更新UI----Thread、H
  6. Android之SharedPreferences详解
  7. React Native调用Android原生组件
  8. android UI进阶之弹窗的使用(2)--实现通讯
  9. Android教程之android数据库编程
  10. Android(安卓)UI学习 - Tab的学习和使用