说到网络库就会想到Google的HttpUrlConnection和Apache的HttpClient。
Google推荐使用HttpUrlConnection,抛弃了HttpClient
HttpClient已在Android6.0被废弃了(吐槽:明明HttpClient功能更强大啊),
查看为什么抛弃HttpClient,可以看下【Android】Android2.3版本以上谷歌为何推荐使用HttpURLConnection却弃用 Apache HttpClient
想继续使用HttpClient的话,可以参考android6.0SDK 删除HttpClient的相关类的解决方法
除了HttpUrlConnection,还有Google推荐的Volley(没怎么用过)
,和square的okhttp
,还有就是今天介绍的square的retrofit
这个年头不会点
retrofit + rxjava + mvp...
的组合拳,都不好意思说是新时代的Android开发
为什么我用的组件都是square公司出品的呢?只能说明他们做的太好了。
回到正题
关于Retrofit的介绍,引用它在github上的一句话
Type-safe HTTP client for Android and Java by Square
用于Android和Java的类型安全的Http框架
现在市面上使用的是retrofit2.0版本,跟以前的1.9版本有部分API不一样,可以查看Retrofit 1.9 迁移到 Retrofit 2.0,建议使用新的版本
Retrofit是使用RESETful API的。
这里简单的介绍下关于RESETful的架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词(GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源),
对服务器端资源进行操作,实现"表现层状态转化"。
更多关于RESET API可以看阮一峰的理解RESTful架构
Retrofit的git地址:https://github.com/square/retrofit
Retrofit的官方地址:http://square.github.io/retrofit/
使用姿势
-
往build.gradle添加依赖,
不需要额外添加okhttp,retrofit内部支持
// retrofit compile 'com.squareup.retrofit2:retrofit:2.2.0' // gson转化器 compile 'com.squareup.retrofit2:converter-gson:2.2.0'
-
创建接口,声明API
// 获取请求API // 请求接口 https://api.github.com public interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Call
- > contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
// 请求对象类
public static class Contributor {
public final String login;
public final int contributions;
public Contributor(String login, int contributions) {
this.login = login;
this.contributions = contributions;
}
}
-
方法调用
public void request() throws IOException { // 1.创建Retrofit对象,根据Retrofit模板创建 Retrofit retrofit = new Retrofit.Builder() // 传入baseurl地址, .baseUrl("https://api.github.com") // 传入gson转化器 .addConverterFactory(GsonConverterFactory.create()) .build(); // 2.获取GitHub的对象 GitHub github = retrofit.create(GitHub.class); // 3.传入参数 Call
- > call = github.contributors("square", "retrofit");
// 4.异步请求接口
// 请求接口格式为"https://api.github.com/repos/square/retrofit/contributors"
call.enqueue(new Callback
- >() {
@Override
public void onResponse(Call
- > call, Response
- > response) {
List
contributors = response.body(); for (Contributor contributor : contributors) { Log.e("MainActivity",contributor.login + " (" + contributor.contributions + ")"); } } @Override public void onFailure(Call - > call, Throwable t) {
}
});
}
通过上面的例子,应该能够理解Retrofit的使用了吧
关于使用,可以参考下官网介绍http://square.github.io/retrofit/
对此,作者菌有几个问题
- Retrofit提供的这么多注解是怎么运用的?
- Retrofit有多少种注解
- Retrofit生成API对象后,怎么发起请求的
源码解读
先查看retrofit的代码构造
.
└── retrofit2
├── BuiltInConverters.java --默认的转化器,支持流,字符串
├── Call.java -- 请求的发送/取消,并获取当前请求的状态
├── CallAdapter.java
├── Callback.java. --回调方法
├── Converter.java -- 转化器,将okhttp中的RequestBody,ResponseBody进行转化,支持XML,JSON,ProtocolBuf,如GsonConverterFactory,将返回的内容或者发送Body中的内容转化,
├── DefaultCallAdapterFactory.java
├── ExecutorCallAdapterFactory.java
├── HttpException.java
├── OkHttpCall.java --实现了Call的接口,使用okhttp3.0的API
├── ParameterHandler.java --参数句柄,处理retrofit头文件的操作请求
├── Platform.java --判断当前运行环境是Android还是Java8
├── RequestBuilder.java
├── Response.java 服务端返回内容,可以通过response.body()获取到返回内容
├── Retrofit.java --`对外方法`
├── ServiceMethod.java
├── Utils.java
├── http --http文件夹中包含的是retrofit所有的注解类,关于Http的操作和上传字段,以及Header配置
│ ├── Body.java 用于Post,根据转换方式将实例对象转化为对应字符串传递参数.比如Retrofit添加GsonConverterFactory则是将body转化为gson字符串进行传递
│ ├── DELETE.java Delete请求
│ ├── Field.java 用于Post方式传递参数,需要在请求接口方法上添加@FormUrlEncoded,即以表单的方式传递参数
│ ├── FieldMap.java
│ ├── FormUrlEncoded.java 同@Field使用
│ ├── GET.java GET请求
│ ├── HEAD.java
│ ├── HTTP.java
│ ├── Header.java 添加http header
│ ├── HeaderMap.java
│ ├── Headers.java 跟@Header作用一样,只是使用方式不一样,@Header是作为请求方法的参数传入,@Headers是以固定方式直接添加到请求方法上
│ ├── Multipart.java 配合@Multipart使用,一般用于文件上传
│ ├── OPTIONS.java
│ ├── PATCH.java
│ ├── POST.java Post请求
│ ├── PUT.java PUT请求
│ ├── Part.java 一般用于文件上传
│ ├── PartMap.java
│ ├── Path.java 用于URL上占位符
│ ├── Query.java 用于Http Get请求传递参数
│ ├── QueryMap.java
│ ├── QueryName.java 用于Http Get请求传递参数
│ ├── Streaming.java 用于Http Get请求传递流形式参数
│ ├── Url.java 直接赋值URL
│ └── package-info.java
└── package-info.java
附上一篇别人写的###Retrofit注解含义###
讲解下Retrofit的使用原理
- 首先讲解Retrofit初始化
Retrofit retrofit = new Retrofit.Builder()
// 传入baseurl地址,
.baseUrl("https://api.github.com")
// 传入gson转化器
.addConverterFactory(GsonConverterFactory.create())
.build();
此处用了Build机制来初始化,现在很多初始化通过Build机制,如Dialog,OKHttp,Picasso等
Build构建时可以传入以下配置
Builder(Platform platform) {
this.platform = platform;
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
}
public Builder() {
// 获取当前的系统版本
this(Platform.get());
}
/**
* 配置Client,默认使用okhttp3.OkHttpClient
* @param client
* @return
*/
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
/**
* 创建CallFactory,用于生成请求Http的Call
* @param factory
* @return
*/
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
/**
* 配置URL
* @param baseUrl
* @return
*/
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
/**
* 配置URL,下面有几组实例配置URL
*
* Correct:
* Base URL: http://example.com/api/
* Endpoint: foo/bar/
* Result: http://example.com/api/foo/bar/
*
* Incorrect:
* Base URL: http://example.com/api
* Endpoint: foo/bar/
* Result: http://example.com/foo/bar/
*
* This method enforces that {@code baseUrl} has a trailing {@code /}.
*
* Endpoint values which contain a leading {@code /} are absolute.
*
* Absolute values retain only the host from {@code baseUrl} and ignore any specified path
* components.
*
* Base URL: http://example.com/api/
* Endpoint: /foo/bar/
* Result: http://example.com/foo/bar/
*
* Base URL: http://example.com/
* Endpoint: /foo/bar/
* Result: http://example.com/foo/bar/
*
* Endpoint values may be a full URL.
*
* Values which have a host replace the host of {@code baseUrl} and values also with a scheme
* replace the scheme of {@code baseUrl}.
*
* Base URL: http://example.com/
* Endpoint: https://github.com/square/retrofit/
* Result: https://github.com/square/retrofit/
*
* Base URL: http://example.com
* Endpoint: //github.com/square/retrofit/
* Result: http://github.com/square/retrofit/ (note the scheme stays 'http')
*/
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
/**
* 添加转换器
* @param factory
* @return
*/
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/**
* Add a call adapter factory for supporting service method return types other than {@link
* Call}.
*/
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/**
* 回调线程
* @param executor
* @return
*/
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this;
}
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
/**
* Create the {@link Retrofit} instance using the configured values.
*
* Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
* OkHttpClient} will be created and used.
*/
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 默认使用okhttp3.0的OKHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 若Platform是Android,回调的是主线程
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
此处注意的是baseUrl,正确写法
是
* Base URL: http://example.com/api/
* Endpoint: foo/bar/
* Result: http://example.com/api/foo/bar/
Platform判断当前运行环境是Android还是JAVA8
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
2.获取GitHub的对象,传入参数
GitHub github = retrofit.create(GitHub.class);
//传入参数
Call> call = github.contributors("square", "retrofit");
通过代理模式来处理接口
public T create(final Class 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);
}
// 加载ServiceMethod,进行URL组装
ServiceMethod
3.调用call.enqueue(new Callback...);
使用的是OKHttpCall,使用了OKHttp的Call,然后进行Http请求。
Call的接口定义
public interface Call extends Cloneable {
/**
* 同步发送请求,返回结果
* @return
* @throws IOException
*/
Response execute() throws IOException;
/**
* 异步发送请求,通过Callback回调返回
* @param callback
*/
void enqueue(Callback callback);
/**
* 是否正在执行发送
* @return
*/
boolean isExecuted();
/**
* 取消发送的请求,若请求还未执行
*/
void cancel();
/**
* 请求是否被取消
* @return
*/
boolean isCanceled();
/**
* Create a new, identical call to this one which can be enqueued or executed even if this call
* has already been.
*/
Call clone();
/** The original HTTP request. */
Request request();
}
4.其他:如何构建Http请求,主要实在ServiceMethod.java这个类中
以@Path为例
else if (annotation instanceof Path) {
if (gotQuery) {
throw parameterError(p, "A @Path parameter must not come after a @Query.");
}
if (gotUrl) {
throw parameterError(p, "@Path parameters may not be used with @Url.");
}
if (relativeUrl == null) {
throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
}
gotPath = true;
Path path = (Path) annotation;
String name = path.value();
validatePathName(p, name);
Converter, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(name, converter, path.encoded());
}
通过刚才的代理模式调用ServiceMethod,将接口中的注解生成完整的Http请求
相关资料
你真的会用Retrofit2吗?Retrofit2完全教程
Retrofit各个注解的含义及作用
Android 网络框架 Retrofit2.0介绍、使用和封装
理解RESTful架构
RESTful API 设计指南
jdk动态代理实现原理