【Android - 框架】之Retrofit的使用

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": "[email protected]",
  "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的工作原理。

时间: 2024-08-28 22:17:16

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

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

前几天分别对Retrofit和RxJava进行了总结,这个帖子打算把Retrofit结合RxJava使用的方法总结以下.有还不了解Retrofit或RxJava的朋友可以参考下面的帖子学习~ [Android - 框架]之Retrofit的使用 [Android - 框架]之RxJava的使用 首先导入依赖: dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.andro

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

面对越来越复杂的 App 需求,Google 官方发布了Android 框架组件库(Android Architecture Components ).为开发者更好的开发 App 提供了非常好的样本.这个框架里的组件是配合 Android 组件生命周期的,所以它能够很好的规避组件生命周期管理的问题.今天我们就来看看这个库的使用. 通用的框架准则 官方建议在架构 App 的时候遵循以下两个准则: 关注分离 其中早期开发 App 最常见的做法是在 Activity 或者 Fragment 中写了大量

一个实用的android框架(一)——架构

原文出处:http://saulmm.github.io/2015/02/02/A%20useful%20stack%20on%20android%20%231,%20architecture/ 原码github地址:https://github.com/saulmm/Material-Movies 作者:Saúl Molinero 译者注:这是最近接触到的一个关于安卓架构的项目,也是基于MVP的,分包上的想法和我比较契合.另外,该项目也是使用了Material Design,感觉比较新颖实用.

在Kotlin中使用注释处理Android框架 kapt

本教程介绍如何在 Kotlin 中使用依赖于注释处理的流行的 Android 框架和库. 在日常 Android 开发中,流行着数以千计的框架帮助我们提升开发效率. 使用 Kotlin 开发时仍然可以沿用这些框架,而且和使用 Java 同样简单. 本章教程将提供相关示例并重点介绍配置的差异. 教程以 Dagger. Butterknife. Data Binding. Auto-parcel 以及 DBFlow 为例(其它框架配置基本类似). 以上框架均基于注解处理方式工作:通过对代码注解自动生

Android框架设计模式(四)——Adapter Method

一适配器模式介绍 什么是适配器模式 定义 分类 适配器应用于什么场景 二Android框架中的适配器模式应用 ListViewBaseAdapter自定义View 通俗UML图 关键代码分析 ActivityBinderMediaPlayer 通俗UML图 关键代码分析 三适配器模式与其他模式的配合 适配器观察者模板策略组合 BaseAdapterListView自定义View 整体UML图 模式分析不同的视角决定 适配器观察者模板 Service Activity 自定义服务 整体UML图 模

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

IOS 和ANDROID操作系统都是目前流行的移动操作系统,被移动终端和智能设备大量采用,两者都采用了先进的软件技术进行设计,为了方便应用开发两者都采用了先进的设计模式.两者在框架设计上都采用了什么技术?都采用了什么设计模式?两者设计思路和应用开发模式有什么异同呢? 两者都采用了框架模式. IOS 的框架称为Cocoa Touch. 框架提供两个作用,一是类的集合,每个类构建一个问题空间,并提供完整的解决方案和服务:二更重要的是框架中的类相互依赖构成一个整体,制订并实现整个应用程序的结构.框架定

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

IOS 和ANDROID操作系统都是眼下流行的移动操作系统,被移动终端和智能设备大量採用,两者都採用了先进的软件技术进行设计,为了方便应用开发两者都採用了先进的设计模式. 两者在框架设计上都採用了什么技术?都採用了什么设计模式?两者设计思路和应用开发模式有什么异同呢? 两者都採用了框架模式. IOS 的框架称为Cocoa Touch. 框架提供两个作用,一是类的集合,每一个类构建一个问题空间,并提供完整的解决方式和服务.二更重要的是框架中的类相互依赖构成一个总体,制订并实现整个应用程序的结构.框

Android 框架启动流程

As we all know,Android手机系统本质上是一个基于Linux的应用程序,它以Linux系统为内核.因此系统的启动过程包括Linux内核启动和Android框架启动两个阶段. Linux内核启动 1.装载引导程序bootloader Linux内核启动时首先装载执行bootloader引导程序,装载完成后进入内核程序. 2.加载Linux内核 Linux内核加载主要包括初始化kernel核心(内存初始化,打开中断,初始化进程表等).初始化驱动.启动内核后台(daemons)线程.

Android框架简要介绍

1.      Android架构直观图 下图展示了Android系统的主要组成部分: 整体上而言,Android系统结构由5个部分组成,从上到下,别人是Applications (Android应用层).ApplicationFramework (Android应用程序框架层).Libraries.Android Runtime (Anroid运行时).Linux Kernel (Linux内核).其中,Libraries和Android Runtime处于Android框架的同一层.下面简要

Android Eclipse中查看 Android框架源码

有时候用Eclipse想按住ctrl键查看源码怎么办? 下面具体步骤让你轻松看源码: project->properties->java build path->libraries 点击android.jar下面的source: 这里可以添加zip和文件夹,zip可以去git下载,我这里用的是用sdk manager下载的源码,如下: 从这里面下载的源码就保存在sdk下面的source下面,选一个平台关联就可以了 下面就是button源码: @RemoteView public clas