Retrofit是Square公司发布的一个可以应用在Android和Java中的Http客户端访问框架,其底层应用的是OkHttp。

  在这个帖子中,我们以下面这个Http请求为例:

https://api.github.com/users/basil2style

  其请求结果(JSON)如下所示:

{
"login": "basil2style",
"id": 1285344,
"avatar_url": "https://avatars.githubusercontent.com/u/1285344?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/basil2style",
"html_url": "https://github.com/basil2style",
"followers_url": "https://api.github.com/users/basil2style/followers",
"following_url": "https://api.github.com/users/basil2style/following{/other_user}",
"gists_url": "https://api.github.com/users/basil2style/gists{/gist_id}",
"starred_url": "https://api.github.com/users/basil2style/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/basil2style/subscriptions",
"organizations_url": "https://api.github.com/users/basil2style/orgs",
"repos_url": "https://api.github.com/users/basil2style/repos",
"events_url": "https://api.github.com/users/basil2style/events{/privacy}",
"received_events_url": "https://api.github.com/users/basil2style/received_events",
"type": "User",
"site_admin": false,
"name": "Basil",
"company": "MakeInfo",
"blog": "http://www.themakeinfo.com",
"location": "Toronto,Canada",
"email": "basiltalias92@gmail.com",
"hireable": true,
"bio": "Developer | Marketer | Reader | Cinephile | Entrepreneur",
"public_repos": 35,
"public_gists": 4,
"followers": 64,
"following": 155,
"created_at": "2011-12-26T00:17:22Z",
"updated_at": "2016-11-12T00:58:15Z"
}

  接下来我们从Retrofit的用法到原理,来介绍一下这个框架。

一、Retrofit的用法:

0、配置Retrofit的开发环境:

  我们在使用Retrofit之前需要先导入Retrofit的包,本DEMO是在Android Studio中开发的,因此只需要在gradle文件中导入依赖即可。下面是Retrofit的依赖:

compile 'com.squareup.retrofit2:retrofit:2.1.0'

  另外,我们从网络中获取到的数据的格式是不同的,可能是JSON/GSON格式,也可能是Jackson、Wire等其他格式,我们需要在后面的编码中用到一个格式转化工厂ConverterFactory,因此我们还需要导入一些有关格式的依赖。本DEMO中使用的是JSON/GSON格式,因此导入相关依赖如下:

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

  其他格式需要导入的依赖列表如下(后面的版本号自己添加):

    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 

  至此,Retrofit的开发环境搭建就完成了。

1、创建一个从网络中获取数据的Service接口:

  Retrofit使用动态代理的方式,将一个网络请求转换成一个接口,用户只需要调用一个接口就可以进行网络访问。这个接口的代码如下:

public interface RetrofitService {
@GET("/users/basil2style")
Call<InfoData> getInfoData();
}

  从上面这段代码中可以看出来, @GET 标签中的值只是这个请求的一部分。一般我们在进行网络请求的时候,大多数请求都是从一个服务器发送的数据,因此我们可以对这些请求的根URL进行统一的管理。在这个DEMO中,我把网络请求的根URL放在SharedData类中:

public class SharedData {
/**
* Base Url
*/
public static final String BASE_URL = "https://api.github.com";
}

  和其他网络请求相同,Retrofit可以通过 @GET 和 @POST 两种方法进行网络请求,这些请求有时候需要携带参数,有时候不需要,上面的getInfoData()方法就是一个不携带任何参数的网络请求方法。当然,这里还需要创建一个Bean类InfoData,用来存储从网络中获取到的数据:

public class InfoData {
private String login;
private int id;
private String avatarUrl;
private String gravatarId;
private String url;
private String htmlUrl;
private String followersUrl;
private String followingUrl;
private String gistsUrl;
private String starredUrl;
private String subscriptionsUrl;
private String organizationsUrl;
private String reposUrl;
private String eventsUrl;
private String receivedEventsUrl;
private String type;
private boolean siteAdmin;
private String name;
private String company;
private String blog;
private String location;
private String email;
private boolean hireable;
private String bio;
private int publicRepos;
private int publicGists;
private int followers;
private int following;
private String createdAt;
private String updatedAt; // Getter、Setter方法
}

  再来介绍一下Call。Call可以用来发送一个请求,Call对象中有两个方法:enqueue()和execute(),前者用来发送一个异步请求,后者又来发送一个同步请求,下面会有代码介绍。

2、初始化Retrofit对象:

  创建Retrofit对象的代码如下:

        Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SharedData.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();

  可以看到,Retrofit对象是通过Retrofit.Builder内部类创建出来的,baseUrl是需要访问的根URL;由于请求的数据是JSON格式的,我们可以使用一个和JSON有关的转换工厂来处理这些数据,这里用到的是和JSON差不多的GSON转化工厂,即GsonConverterFactory。

3、调用Retrofit对象进行网络访问:

  获取到Retrofit对象后,我们通过这个对象获取到网络请求接口RetrofitService,再调用其中的getInfoData()方法获取到网络数据即可。具体代码如下:

        RetrofitService service = retrofit.create(RetrofitService.class);

        Call<InfoData> call = service.getInfoData();
call.enqueue(new Callback<InfoData>() {
@Override
public void onResponse(Call<InfoData> call, Response<InfoData> response) {
InfoData data = response.body();
Message message = Message.obtain();
message.what = 1;
message.obj = data;
handler.sendMessage(message);
} @Override
public void onFailure(Call<InfoData> call, Throwable t) {
handler.sendEmptyMessage(0);
}
});

  这里还需要说明一下,Retrofit不像Volley可以直接把异步数据拉回到主线程,Retrofit中Callback类的onResponse()方法仍然是在异步线程中的,如果我们要将数据拿到主线程,需要使用AsyncTask或Handler,本DEMO中使用的是Handler,以下是Handler中的代码:

    private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
InfoData data = (InfoData) msg.obj;
Toast.makeText(MainActivity.this, data.getBlog(), Toast.LENGTH_SHORT).show();
break;
case 0:
Toast.makeText(MainActivity.this, "获取数据失败", Toast.LENGTH_SHORT).show();
break;
}
}
};

  到此为止Retrofit访问网络获取数据的DEMO就完成了,运行结果如下图所示:

二、RetrofitService接口方法种类:

  上面的RetrofitService接口中的getInfoData()方法只是一个没有携带任何参数的GET请求,我们在访问网络数据的时候有时需要使用POST请求,并且可能会携带一些参数等,下面来逐个介绍一下这些情况下接口方法的编写方式。

  特别说明:下面访问的接口和上面DEMO中的接口没有任何关系,两两之间也没有任何关系!

1、没有参数的GET请求:

    @GET("users/list")
Call<List<User>> getUserList();

2、有参数的GET请求:

(1)第一种方式:在GET标签中添加参数:

    @GET("users/list?sort=desc")
Call<List<User>> getUserList();

(2)第二种方式:在方法参数中设置参数:

    @GET("users/list")
Call<List<User>> getUserList(@Query("sort") String sort); 

3、在GET标签中设置路径参数:

    @GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

4、传入参数列表:

    @GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

5、POST方式请求:

    @POST("users/new")
Call<User> createUser(@Body User user);

三、Retrofit原理:

  Retrofit用注解来描述一个网络请求,将一个网络请求抽象成一个接口,然后使用Java动态代理的方式动态的将这个接口“翻译”成一个网络请求,最后调用OkHttp去执行这个请求。

  Retrofit中的动态代理主要体现在下面这行代码:

RetrofitService service = retrofit.create(RetrofitService.class);

  create()方法的源码如下:

/** Create an implementation of the API defined by the {@code service} interface. */
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(); 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 serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

  可见,所谓的动态代理,就是给程序员一种可能:在执行某个操作(如打开某个Class)之前,插入一段你想要执行的代码。比如你在执行某个操作之前判断用户是否已经登录,等。

  当外界(Activity)通过create()方法打开这个接口并调用其中的方法时,底层就调用了上面这段代码中的invode()方法。这个方法是通过Java反射机制获取到接口中的getInfoData()方法,用这个方法作为参数创建一个ServiceMethod对象,最后使用OkHttp的API调用进行网络访问。

  总结一下:Retrofit使用注解的方式将一个网络请求封装成一个接口,然后使用动态代理的方法,在插入的代码中通过反射机制找到接口中的方法,封装到OkHttp中进行网络访问,最后对网络访问得到的Call对象进行enqueue()或execute()操作,在回调的方法中处理网络获取到的数据。

  以上就是Retrofit的工作原理。

【Android - 框架】之Retrofit的使用的更多相关文章

  1. 【Android - 框架】之Retrofit+RxJava的使用

    前几天分别对Retrofit和RxJava进行了总结,这个帖子打算把Retrofit结合RxJava使用的方法总结以下.有还不了解Retrofit或RxJava的朋友可以参考下面的帖子学习~ [And ...

  2. App 组件化/模块化之路——Android 框架组件(Android Architecture Components)使用指南

    面对越来越复杂的 App 需求,Google 官方发布了Android 框架组件库(Android Architecture Components ).为开发者更好的开发 App 提供了非常好的样本. ...

  3. Android 框架简介--Java环境(转)

    ==========================上=========================== 这里简单的介绍了Android的java环境基础,在后面一节中会结合具体的实例来理解这一节 ...

  4. 【Android - 框架】之GreenDao的使用

    上一篇博客([Android - 框架]之ORMLite的使用)中介绍了ORMLite的基本使用,今天我们来研究以下GreenDao的使用. GreenDao和ORMLite一样,都是基于ORM(Ob ...

  5. IOS 与ANDROID框架及应用开发模式对照一

    IOS 和ANDROID操作系统都是眼下流行的移动操作系统,被移动终端和智能设备大量採用,两者都採用了先进的软件技术进行设计,为了方便应用开发两者都採用了先进的设计模式. 两者在框架设计上都採用了什么 ...

  6. Kotlin的扩展函数:扩展Android框架(KAD 08)

    作者:Antonio Leiva 时间:Jan 11, 2017 原文链接:https://antonioleiva.com/extension-functions-kotlin/ 扩展函数是Kotl ...

  7. 【Android 系统开发】Android框架 与 源码结构

    一. Android 框架 Android框架层级 : Android 自下 而 上 分为 4层; -- Linux内核层; -- 各种库 和 Android运行环境层; -- 应用框架层; -- 应 ...

  8. IOS 与ANDROID框架及应用开发模式对比一

    IOS 和ANDROID操作系统都是目前流行的移动操作系统,被移动终端和智能设备大量采用,两者都采用了先进的软件技术进行设计,为了方便应用开发两者都采用了先进的设计模式.两者在框架设计上都采用了什么技 ...

  9. Android 框架式编程 —— 起篇

    一般的,在开发的时候,写过的代码在需求变更后,发现需要改动非常多的地方,那么说明之前的代码的架构肯定是存在问题的. 下面我们结合面向对象的六大基本原则谈Android 框架式编程.首先先介绍一下面向对 ...

  10. android 框架层 常用类介绍

    名称 功能描述 示意图 activitymanager 管理应用程序的周期并提供常用的回退功能 window manager 窗口管理者 content provider 用于访问另一个的数据,或者共 ...

随机推荐

  1. 24种设计模式--建造者模式【Builder Pattern】

    在一个周三,快要下班了,老大突然又拉住我,喜滋滋的告诉我“牛叉公司很满意我们做的模型,又签订了一个合同,把奔驰.宝马的车辆模型都交给我们公司制作了,不过这次又额外增加了一个新需求:汽车的启动.停止.喇 ...

  2. 调整ListBox控件的行间距及设置文本格式

    首先要将该控件的DrawMode属性为OwnerDrawVariable 添加DrawItem重绘事件:private void listBox1_DrawItem(object sender, Dr ...

  3. ubuntu使用github

    Ubuntu下安装Git Ubuntu12.04 LTS默认是已经安装Git的,可以使用 git --version 测试是否安装.如果没有安装,使用命令: sudo apt-get install ...

  4. 设置(TableViewController)通用框架

    本文学习于传播播客.李明杰老师.感谢

  5. [python]获取字符串类型

    >>>type(value) <class 'type'> >>>isinstance(value,type) True/False

  6. 字符串copy

    #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string. ...

  7. http server v0.1_http_reponse.c

    #include <string.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl ...

  8. 转:char*, char[] ,CString, string的转换

    转:char*, char[] ,CString, string的转换 (一) 概述 string和CString均是字符串模板类,string为标准模板类(STL)定义的字符串类,已经纳入C++标准 ...

  9. German Collegiate Programming Contest 2013:B

    一个离散化的简单题: 我用的是STL来做的离散化: 好久没写离散化了,纪念一下! 代码: #include<cstdio> #include<cstring> #include ...

  10. html5自定义标签选择器

    * E[attr]:只使用属性名,但没有确定任何属性值 * E[attr="value"]:指定属性名,并指定了该属性的属性值.必须和元素的属性完全匹配 * E[attr~=&qu ...