Android 网络框架 Retrofit2.0介绍、使用和封装

##前言
时至今日,Android的网络框架不再像之前那么到处都是,随着Google把 HttpClient直接删掉,似乎意味着Android越来越成熟。网络框架中的佼佼者Volley也不再那么光鲜,取而代之的是 Retrofit 和 okHttp。
感觉很像 OnePiece 中白胡子的离去象征着时代的变革,新时代的开始,多弗的垮台象征着七武海制度的取缔一样,不会使用Retrofit + okHttp + RxJava等一系列技术,就迈不进新时代的门槛,也不足以称为一个合格的开发者。

哈哈闲话不多说了,只是在 Android这个平台上 开发这么长时间的一点感触。各位看看笑笑也就算了。还是来介绍 Retrofit 吧。(直接略过1.9)

本文的所有代码在 retrofitLearn;

##1. Retrofit介绍
A type-safe HTTP client for Android and Java
一个用于Android和Java平台的类型安全的网络框架

*Retrofit is a type-safe REST client for Android built by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp. *
Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 。

Retrofit 把REST API返回的数据转化为Java对象,就像ORM框架那样,把数据库内的存储的数据转化为相应的Java bean对象。

那么我们知道Retrofit是一个类型安全的网络框架,而且它是使用REST API的,接下来我们看看什么是REST吧。

##2. REST 介绍:
Resources Representational State Transfer
资源表现层状态转化

  • 每一个URI代表一种资源
  • 客户端和服务器之间,传递这种资源的某种 表现层("资源"具体呈现出来的形式,比如.txt,.png,.jpg)
  • 客户端通过四个HTTP动词(GET用来获取资源,POST用来新建或更新资源,PUT用来更新资源,DELETE用来删除资源)对服务器端资源进行操作,实现"表现层状态转化"

关于REST,这里只仅仅列出了结论,文末有超级好的链接,请查看。如果你所使用的API是REST的,那么恭喜你,这样的API看起来真的很舒服,庆幸的是我司就使用的是REST API。

知道了REST是什么,那接下啦就开始介绍Retrofit的用法啦。

##3. Retrofit基本用法(未封装)
1.在build.gradle中添加依赖

//okHttp
compile 'com.squareup.okhttp3:okhttp:3.2.0'

//retrofit
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

2.创建接口,声明API

//Retrofit turns your HTTP API into a Java interface.
//创建接口,声明GitHub的API
public interface GitHubAPI {

	 /*
	   请求该接口:https://api.github.com/users/baiiu
	 */
	 @GET("users/{user}") 
	 Call userInfo(@Path("user") String user);
}

3.在MainActivity.onCreate()方法中调用

/*
1.初始化OkHttpClient
*/
OkHttpClient client = new OkHttpClient()/*
2.创建Retrofit
*/
retrofit = new Retrofit.Builder()
        //设置OKHttpClient
        .client(client)
        //设置baseUrl,注意,baseUrl必须后缀"/"
        .baseUrl("https://api.github.com/")
            
		//添加Gson转换器
		.addConverterFactory(GsonConverterFactory.create())
        .build();

/*
2.获取GitHub的API
*/
GitHubAPI gitHubAPI = retrofit.create(GitHubAPI.class);

/*
3.异步调用
*/
Call<User> userCall = gitHubAPI.userInfo("baiiu");
userCall.enqueue(new Callback<User>() {
      @Override public void onResponse(Call<User> call, Response<User> response) {
        User body = response.body();
        LogUtil.d(body == null ? "body == null" : body.toString());
      }

      @Override public void onFailure(Call<User> call, Throwable t) {
	    /*
	     判断是否被cancel掉了
	    */
        if (call.isCanceled()) {
          LogUtil.d("the call is canceled , " + toString());
        } else {
          LogUtil.e(t.toString());
        }
      }
    });

/*
取消调用
*/
//userCall.cancel();

/*
同步调用,举个例子,写法如下(当然不能在主线程调用):
*/


//Each instance can only be used once, but calling clone() will create a new instance that can be used.
Call<User> clone = userCall.clone();

Response<User> response = clone.execute();
User body = response.body();
LogUtil.d(body == null ? "body == null" : body.toString());

注意:
    1. 设置BaseUrl时必须后缀"/",如 https://api.github.com/,这篇文章说的超清晰:Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android

     2. 因为Retrofit在创建时候传入了BaseUrl,所以基本上所有请求都基于该BaseUrl了。但是总有些API不是以该BaseUrl开头的,特别是有些公司可能不是restful API的。那么该怎么办呢。Retrofit提供了一个注解@Url解决这个问题,可以在运行时直接使用该Url直接访问。代码如下:

//使用@Url注解,传入该Url地址就OK啦,跨过BaseUrl,直接访问该Url地址
@GET Call getNewsList(@Url String url);

恩,接下来,知道了Retrofit的基本使用,接下来就是介绍Retrofit的注解了。

##4. Retrofit注解
Retrofit使用注解来声明API的请求方式、请求参数等。这里只介绍一下它的 @Header 注解,其他的请阅读 官网文档 和 鸿洋的 Retrofit2 完全解析 探索与okhttp之间的关系。和Retrofit 2.0 注解篇
(这块写了也是翻译官方文档的和参考鸿样的,会拉长篇幅)

请求头的设置可以通过 @Header 注解添加,又有两种添加方式:

  • 设置静态的请求头。
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call> widgetList();

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call getUser(@Path("username") String username);
  • 动态的设置请求头。
@GET("user")
Call getUser(@Header("Authorization") String authorization)

Note that headers do not overwrite each other. All headers with the same name will be included in the request.
同一个请求的同一个请求头在不同地方的设置不会被覆盖,而是会被全部添加进请求头中。

Headers that need to be added to every request can be specified using an OkHttp interceptor.
如果要给每个请求都添加同样的Header时,可以使用okHttp的 Interceptor

那么,接下来就介绍Interceptor。

##5.Interceptors使用
Retrofit 2.0 底层强制依赖okHttp,所以可以使用okHttp的拦截器Interceptors 来对所有请求进行再处理。

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.

使用时,实现 Interceptor 接口,做我们自己的处理。
目前使用中,一般用来设置UA设置缓存策略打印Log 等。这里介绍一个设置UA的Interceptor:

1.创建设置UA的Interceptor

public final class UserAgentInterceptor implements Interceptor {
  private static final String USER_AGENT_HEADER_NAME = "User-Agent";
  private final String userAgentHeaderValue;

  public UserAgentInterceptor(String userAgentHeaderValue) {
    this.userAgentHeaderValue = userAgentHeaderValue;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    final Request originalRequest = chain.request();

    final Request requestWithUserAgent = originalRequest.newBuilder()

        //移除先前默认的UA
        .removeHeader(USER_AGENT_HEADER_NAME)

        //设置UA
        .addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)


        .build();
    return chain.proceed(requestWithUserAgent);
  }
}

2.创建okHttpClient时 设置该Interceptor

okHttpClient = new OkHttpClient.Builder()
        //添加UA
        .addInterceptor(new UserAgentInterceptor(HttpHelper.getUserAgent()))

        //失败重连
        .retryOnConnectionFailure(true)

        //time out
        .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
        .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)

        .build();

Interceptors 和 Header 的配合使用让我们很方便的使用Http本身的缓存。之前用Volley的时候这块就比较难处理。解下来就说说Retrofit的缓存机制。

##6. 配置网络缓存
使用Retrofit 感觉最方便的就是它提供的缓存方式(虽然这是Http本身的),我们可以随便通过Header和Interceptor配置自己的缓存策略。

###6.1 缓存设置原理:
缓存的设置是通过设置 Http请求头和响应头中的 Cache-Controlmax-age 属性达到的。在复写 Interceptor 时,通过设置响应头的 Cache-Control 来达到目的。关于Http的本身的缓存命中请查看文末链接 (毕竟Http也是个大家伙) 。

6.2 缓存设置步骤

  1. 设置缓存目录
    retrofit本身默认无缓存,连缓存目录都没有提供默认的,所以 要想实现缓存,必须要在创建OkHttpClient时设置缓存目录。 这块超级坑,自己试了半天,最终看了下Cache那边的源码,竟然没有默认的缓存目录!!!

  2. 设置缓存的方式
    - 通过添加 @Headers("Cache-Control: max-age=120") 进行设置。添加了Cache-Control 的请求,retrofit 会默认缓存该请求的返回数据。
    - 通过Interceptors实现缓存。

6.3 实现Interceptor进行缓存

提供了两种缓存策略(参考),设置缓存时Application Interceptor 和 Net Interceptor 都要设置:

  • AllCachedInterceptor
    有网没网都先走缓存,可以设置间歇时间。
    同时可以针对某一个request设置单独的缓存时间,使用 @Headers("Cache-Control: max-age=120") 就行。

  • OnOffLineCachedInterceptor
    有网可以不走缓存,可设定时间,设置为0意味着有网不走缓存,全部刷新数据;无网强制读取缓存数据。

到现在为止,关于Retrofit的基本用法和缓存设置已经了解了,写点例子就已经会用了,接下来肯定就是进行封装了,让它更便于调用。

##7. Retrofit 封装
封装Retrofit是为了提供更方便的调用,更好的配置和使用。那么看到开头的Retrofit调用方式,大概有这么几个步骤:

  1. 创建OkHttpClient
  2. 创建Retrofit实例
  3. 获取我们写的API interface
  4. 在代码中异步调用

那么封装时候也肯定从这几步入手:

###7.1 初始化OkHttpClient
创建OkHttpFactory类,初始化OkHttpClient并对外提供该实例的单例。并提供一个 HttpHelper 类用于提供所需要的参数,比如UA。
这样做的好处是,程序中会使用OkHttp做一些其他请求操作,比如下载、上传等网络操作,就可用共用一个OkHttpClient。

这里使用枚举生成单例。

enum OKHttpFactory {

  INSTANCE;

  private final OkHttpClient okHttpClient;

  private static final int TIMEOUT_READ = 25;
  private static final int TIMEOUT_CONNECTION = 25;

  OKHttpFactory() {
	//打印请求Log
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

	//缓存目录
    Cache cache = new Cache(MyApplication.mContext.getCacheDir(), 10 * 1024 * 1024);

    okHttpClient = new OkHttpClient.Builder()
        //打印请求log
        .addInterceptor(interceptor)

        //stetho,可以在chrome中查看请求
        .addNetworkInterceptor(new StethoInterceptor())

        //添加UA
        .addInterceptor(new UserAgentInterceptor(HttpHelper.getUserAgent()))

        //必须是设置Cache目录
        .cache(cache)

        //走缓存,两个都要设置
        .addInterceptor(new OnOffLineCachedInterceptor())
        .addNetworkInterceptor(new OnOffLineCachedInterceptor())

        //失败重连
        .retryOnConnectionFailure(true)

        //time out
        .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
        .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)

        .build();
  }

  public OkHttpClient getOkHttpClient() {
    return okHttpClient;
  }
}

###7.2 初始化Retrofit
依然使用枚举创建单例:

public enum RetrofitClient implements ApiContants {
  INSTANCE;

  private final Retrofit retrofit;

  RetrofitClient() {
    retrofit = new Retrofit.Builder()
        //设置OKHttpClient
        .client(OKHttpFactory.INSTANCE.getOkHttpClient())

        //baseUrl
        .baseUrl(GITHUB_BASEURL)

        //gson转化器
        .addConverterFactory(GsonConverterFactory.create())

        .build();
  }

  public Retrofit getRetrofit() {
    return retrofit;
  }
}

###7.3 创建ApiFactory类封装所有API
如题,创建ApiFactory类管理所有的API interface,对外提供方法获取他们,这样调用时会方便很多,而且也便于修改。

public enum ApiFactory {
  INSTANCE;

  private final GitHubAPI gitHubAPI;
  private final AnotherAPI anotherAPI;

  ApiFactory() {
    gitHubAPI = RetrofitClient.INSTANCE.getRetrofit().create(GitHubAPI.class);
    anotherAPI = RetrofitClient.INSTANCE.getRetrofit().create(AnotherAPI.class);
  }

  public GitHubAPI gitHubAPI() {
    return gitHubAPI;
  }

  public AnotherAPI getAnotherAPI() {
    return anotherAPI;
  }
}

这样封装在外部调用时会方便很多,比如:

    Call userCall = ApiFactory.gitHubAPI().userInfo("baiiu");

封装的代码在GitHub上:retrofitLearn;

这样封装自己觉得还不错,就可以开心的写代码啦。接下来说说一个神奇的工具。

8. Stetho 一个神奇的工具

首先:使用该工具需要。 不能的还是使用Charles吧。

  1. gradle中添加:
  compile 'com.facebook.stetho:stetho:1.3.1'
  compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'
  1. 打开chrome,输入chrome://inspect
    点击你的程序进行inspect,就可以直接使用Chrome进行抓包、调试你的应用啦。但是不能的话,你会看到一片空白。。。

##结语:
毕竟Retrofit出来已经很久了,没有的赶紧用用吧。在使用Retrofit时,深深赶紧到其对Http本身机制的应用之深,真的感觉到非常棒的应用体验。用Retrofit作为网络框架,简直就能感觉到它设计之美,更何况还能和RxJava结合起来,美到不行。


参考:这些参考都值得一看的

  • 关于Retrofit的一本书
    Retrofit — Basic Authentication on Android
  • retrofit介绍:
    retrofit官网
    Consuming APIs with Retrofit
    Retrofit2 完全解析 探索与okhttp之间的关系
    Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android
    你真的会用Retrofit2吗?Retrofit2完全教程
  • Restful API介绍
    理解RESTful架构
    深入浅出REST
  • OkHttp
    effective okHttp
    Android OkHttp完全解析 是时候来了解OkHttp了
    Android 一个改善的okHttp封装库
  • Interceptors
    wiki-Interceptors
    wiki-Interceptors 中文
  • 配置缓存
    Retrofit2.0+okHttp3缓存机制以及遇到的问题
    使用Retrofit和OkHttp实现网络缓存。无网读缓存,有网根据过期时间重新请求
    浏览器 HTTP 缓存原理分析
    浏览器缓存
  • 其他助于理解的
    四种常见的 POST 提交数据方式
  • stetho
    解决provisional headers are shown的过程

你可能感兴趣的:(Android,Java等基础知识)