Retrofit笔记

来源:互联网 发布:linux 永久关闭 swap 编辑:程序博客网 时间:2024/05/17 23:22

Retrofit是什么

来自Retrofit官网的介绍:

Type-safe HTTP client for Android and Java by Square, Inc.
For more information please see the website

因为其简单与出色的性能,Retrofit 是安卓上最流行的HTTP Client库之一。

不过它的缺点是在Retrofit 1.x中没有直接取消正在进行中任务的方法。如果你想做这件事必须手动杀死,而这并不好实现。

Square几年前曾许诺这个功能将在Retrofit 2.0实现,但是几年过去了仍然没有在这个问题上有所更新。

直到不久前,Retrofit 2.0才从候选发布阶段变成Beta 1,并且公开给所有人(现在最新版本是beta 2版)。在尝试了之后,我不得不说自己对新的模式和新的功能印象深刻。有许多改进,本文将讨论它们。让我们开始吧!

导包

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'

上面那个包是目前最新的,不建议直接使用不要偷懒,还是去github官网看看吧。

Retrofit怎么用

虽然Retrofit官网已经说明了,我还是要按照我的思路说一下它的使用方法

比如你要请求这么一个api:

https://api.github.com/repos/{owner}/{repo}/contributors

查看github上某个repo的contributors,首先你要这样建一个接口:

public interface GitHub {       @GET("/repos/{owner}/{repo}/contributors")       Call<List<Contributor>> contributors(               @Path("owner") String owner,               @Path("repo") String repo);}

然后你还需要创建一个Retrofit 对象:

public static final String API_URL = "https://api.github.com";// Create a very simple REST adapter which points the GitHub API.Retrofit retrofit = new Retrofit.Builder()                         .baseUrl(API_URL)                         .addConverterFactory(GsonConverterFactory.create())                         .build();

再用这个Retrofit对象创建一个GitHub对象:

// Create an instance of our GitHub API interface.GitHub github = retrofit.create(GitHub.class);// Create a call instance for looking up Retrofit contributors.Call<List<Contributor>> call = github.contributors("square", "retrofit");

最后你就可以用这个Githb对象获得数据了:

// Fetch and print a list of the contributors to the library.call.enqueue(new Callback<List<Contributor>>() {      @Override     public void onResponse(Response<List<Contributor>> response) {          for (Contributor contributor : response.body()) {              System.out.println(contributor.login + " (" + contributor.contributions + ")");        }     }     @Override      public void onFailure(Throwable t) {     }});

你还可以移除一个请求:
Retrofit 1.x版本没有直接取消正在进行中任务的方法的。在2.x的版本中,Service 的模式变成Call的形式的原因是为了让正在进行的事务可以被取消。要做到这点,你只需调用call.cancel()。

call.cancel();

事务将会在之后立即被取消。

总而言之,Retrofit使用方式看上去和Volley的方式完全不一样,使用Volley时你必须先创建一个Request对象,包括这个请求的Method,Url,Url的参数,以及一个请求成功和失败的Listener,然后把这个请求放到RequestQueue中,最后NetworkDispatcher会请求服务器获得数据。而Retrofit只要创建一个接口就可以了,是不是太不可思议了!!

其实万变不离其宗,这两种方式本质上是一样的,只是这个框架描述HTTP请求的方式不一样而已。因此,你可以发现上面的Github接口其实就是Retrofit对一个HTTP请求的描述

以上就是Retrofit的基本用法,下面就来讲讲它的详细用法。

URL的定义方式

Retrofit 2.0使用了新的URL定义方式。Base URL与@Url 不是简单的组合在一起而是和<a href="...">的处理方式一致。用下面的几个例子阐明。

public interface APIService {        @POST("user")        Call<User> login();}public void doSomething() {        Retrofit retrofit = new Retrofit.Builder()            .baseUrl("http://api.demo.come/base/home")            .addConverterFactory(GsonConverterFactory.create())            .build();}//最后的url是http://api.demo.come/base/user
public interface APIService {        @POST("user")        Call<User> login();}public void doSomething() {        Retrofit retrofit = new Retrofit.Builder()            .baseUrl("http://api.demo.come/base/home/")            .addConverterFactory(GsonConverterFactory.create())            .build();}//最后的url是http://api.demo.come/base/home/user
public interface APIService {        @POST("/user")        Call<User> login();}public void doSomething() {        Retrofit retrofit = new Retrofit.Builder()            .baseUrl("http://api.demo.come/base/home/")            .addConverterFactory(GsonConverterFactory.create())            .build();}//最后的url是http://api.demo.come/user

不知道大家看懂了没有,没看懂的话多看几遍吧,注意baseurl和注解中的反斜杠的变化

ps:貌似第二个才符合习惯。
对于 Retrofit 2.0中新的URL定义方式,这里是我的建议:

  • Base URL: 总是以 /结尾
  • @Url: 不要以 / 开头

比如:

public interface APIService {        @POST("user/list")        Call<User> login();}public void doSomething() {        Retrofit retrofit = new Retrofit.Builder()            .baseUrl("http://api.demo.come/base/")            .addConverterFactory(GsonConverterFactory.create())            .build();}//最后的url是http://api.demo.come/base/user/list

而且在Retrofit 2.0中我们还可以在@Url里面定义完整的URL:

public interface APIService {        @POST("http://api.demo.come/base/user/list")        Call<User> login();}

这种情况下Base URL会被忽略。

解析

在Retrofit 2.0中,Converter 不再包含在package 中了。你需要自己插入一个Converter 不然的话Retrofit 只能接收字符串结果。同样的,Retrofit 2.0也不再依赖于Gson 。

如果你想接收json 结果并解析成DAO,你必须把Gson Converter 作为一个独立的依赖添加进来。

compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

然后使用addConverterFactory把它添加进来。注意RestAdapter的别名仍然为Retrofit。

Retrofit retrofit = new Retrofit.Builder()        .baseUrl("http://api.demo.come/base/")        .addConverterFactory(GsonConverterFactory.create())        .build();service = retrofit.create(APIService.class);

这里是Square提供的官方Converter modules列表。选择一个最满足你需求的。

Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml

你也可以通过实现Converter.Factory接口来创建一个自定义的converter 。
我比较赞同这种新的模式。它让Retrofit对自己要做的事情看起来更清晰。

自定义Gson对象
为了以防你需要调整json里面的一些格式,比如,Date Format。你可以创建一个Gson 对象并把它传递给GsonConverterFactory.create()。

Gson gson = new GsonBuilder()        .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")        .create();Retrofit retrofit = new Retrofit.Builder()        .baseUrl("http://api.demo.come/base/")        .addConverterFactory(GsonConverterFactory.create(gson))        .build();service = retrofit.create(APIService.class);

完成。

现在需要OkHttp的支持
在Retrofit 2.0中,OkHttp 是必须的,并且自动设置为了依赖。下面的代码是从Retrofit 2.0的pom文件中抓取的。你不需要再做任何事情了。

<dependencies>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
</dependency>
...
</dependencies>

为了让OkHttp 的Call模式成为可能,在Retrofit 2.0中OkHttp 自动被用作HTTP 接口。

se即使response存在问题onResponse依然被调用

在Retrofit 1.9中,如果获取的 response 不能背解析成定义好的对象,则会调用failure。但是在Retrofit 2.0中,不管 response 是否能被解析。onResponse总是会被调用。但是在结果不能背解析的情况下,response.body()会返回null。别忘了处理这种情况。

如果response存在什么问题,比如404什么的,onResponse也会被调用。你可以从response.errorBody().string()中获取错误信息的主体。


1.jpg

缺少INTERNET权限会导致SecurityException异常
在Retrofit 2.0中,当你调用call.enqueue或者call.execute,将立即抛出SecurityException,如果你不使用try-catch会导致崩溃。


1.png


这类似于在手动调用HttpURLConnection时候的行为。不过这不是什么大问题,因为当INTERNET权限添加到了 AndroidManifest.xml中就没有什么需要考虑的了。

Use an Interceptor from OkHttp
我们必须使用OkHttp里面的Interceptor。首先你需要实用Interceptor创建一个OkHttpClient对象,如下:

OkHttpClient client = new OkHttpClient();client.interceptors().add(new Interceptor() {    @Override    public Response intercept(Chain chain) throws IOException {        Response response = chain.proceed(chain.request());        // Do anything with response here        return response;    }});

然后传递创建的client到Retrofit的Builder链中。

Retrofit retrofit =  new Retrofit.Builder()        .baseUrl("http://api.demo.come/base/")                .addConverterFactory(GsonConverterFactory.create())                .client(client)                .build();

以上为全部内容。
学习关于OkHttp Interceptor的知识,请到OkHttp Interceptors。

终于到放大招的时候啦,RxJava支持
RxJava 是笔者当前最喜欢的一种编程方式要学习的可以去这里。

RxJava Integration with CallAdapter
除了使用Call模式来定义接口,我们也可以定义自己的type,比如MyCall。。我们把Retrofit 2.0的这个机制称为CallAdapter。

Retrofit团队有已经准备好了的CallAdapter module。其中最著名的module可能是为RxJava准备的CallAdapter,它将作为Observable返回。要使用它,你的项目依赖中必须包含两个modules。

compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'compile 'io.reactivex:rxandroid:1.1.0'

Sync Gradle并在Retrofit Builder链表中如下调用addCallAdapterFactory:

Retrofit retrofit =  new Retrofit.Builder()        .baseUrl("http://api.demo.come/base/")                .addConverterFactory(GsonConverterFactory.create())             .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                .build();

你的Service接口现在可以作为Observable返回了!

你可以完全像RxJava那样使用它,如果你想让subscribe部分的代码在主线程被调用,需要把observeOn(AndroidSchedulers.mainThread())添加到链表中。

Observable<DessertItemCollectionDao> observable = service.loadDessertListRx();observable.observeOn(AndroidSchedulers.mainThread())    .subscribe(new Subscriber<DessertItemCollectionDao>() {        @Override        public void onCompleted() {            Toast.makeText(getApplicationContext(),                    "Completed",                    Toast.LENGTH_SHORT)                .show();        }        @Override        public void onError(Throwable e) {            Toast.makeText(getApplicationContext(),                    e.getMessage(),                    Toast.LENGTH_SHORT)                .show();        }        @Override        public void onNext(DessertItemCollectionDao dessertItemCollectionDao) {            Toast.makeText(getApplicationContext(),                    dessertItemCollectionDao.getData().get(0).getName(),                    Toast.LENGTH_SHORT)                .show();        }    });

完成!我相信RxJava的粉丝对这个变化相当满意。

Retrofit的原理

Volley描述一个HTTP请求是需要创建一个Request对象,而执行这个请求呢,就是把这个请求对象放到一个队列中,让网络线程去处理。

Retrofit是怎么做的呢?答案就是Java的动态代理

动态代理
当开始看Retrofit的代码,我对下面这句代码感到很困惑:

// Create an instance of our GitHub API interface.GitHub github = retrofit.create(GitHub.class);

我给Retrofit对象传了一个Github接口的Class对象,怎么又返回一个Github对象呢?进入create方法一看,没几行代码,但是我觉得这几行代码就是Retrofit的精妙的地方:

/** Create an implementation of the API defined by the {@code service} interface. */@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.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();          @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);            }            return loadMethodHandler(method).invoke(args);          }     });}

看,create方法重要就是返回了一个动态代理对象。那么问题来了...

动态代理是个什么东西?

看Retrofit代码之前我知道Java动态代理是一个很重要的东西,比如在Spring框架里大量的用到,但是它有什么用呢?

Java动态代理就是Java开发给了开发人员一种可能:当你要调用某个类的方法前,插入你想要执行的代码

比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱。这么简单的一句话,我相信可以把一个不懂技术的人也讲明白Java动态代理是什么东西了。

为什么要使用动态代理
你看上面代码,获取数据的代码就是这句:

// Create a call instance for looking up Retrofit contributors.Call<List<Contributor>> call = github.contributors("square", "retrofit");

上面github对象其实是一个动态代理对象,并不是一个真正的Githb接口的implements对象,当github对象调用contributors方法时,执行的是动态代理方法(你debug一下就知道了)

此时,动态代理发挥了它的作用,你看上去是调用了contributors方法,其实此时Retrofit把Github接口翻译成一个HTTP请求,也就是Retrofit中的MethodHandler对象,这个对象中包含了:

  • OkHttpClient:发送网络请求的工具
  • RequestFactory: 类似于Volley中的Request,包含了HTTP请求的Url、Header信息,MediaType、Method以及RequestAction数组
  • CallAdapter:HTTP请求返回数据的类型
  • Converter:数据转换器

简单来说,Retrofit就是在你调用Call<List<Contributor>> call = github.contributors("square", "retrofit");后为你生成了一个Http请求,然后,你调用call.enqueue方法时就发送了这个请求,然后你就可以处理Response的数据了,从原理上讲,就是这样的。如果要再往细节处说,就可以再说很多了



文/换个发型换种丑(简书作者)
原文链接:http://www.jianshu.com/p/90b1f20b123d
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子总是写错字怎么办 小孩总是写错字怎么办 孩子马虎不认真怎么办 小孩计算老出错怎么办 小学生计算总出错怎么办 高中考试总粗心怎么办 孩子考试总是粗心怎么办 一年级孩子考试粗心怎么办 老师说孩子粗心怎么办 计算题总出错怎么办 计算题总是出错怎么办 数学老是计算错误怎么办? 苗木抵扣计算错误怎么办 表格中计算错误怎么办 孩子电视瘾太大怎么办 孩子考试前失眠怎么办 孩子上网瘾了怎么办 孩子考试差家长怎么办 手指肿痛有脓包怎么办 手指脓出来红肿怎么办 手指提重物发麻怎么办 小孩拧伤了怎么办 从小就成绩差怎么办 孩子考试总马虎怎么办 上课不认真怎么办初中 对学习兴趣不大怎么办 小学生做作业粗心大意怎么办 小学生考试总是粗心大意怎么办 脚趾内侧长鸡眼怎么办 五岁宝宝胆小怎么办 孩子偏食挑食老师怎么办 九岁孩没有耐心怎么办 孩子学习太粗心怎么办 害怕自己变疯怎么办 工作太粗心了怎么办 粗心的宝宝该怎么办 孩子身上起范怎么办 又笨又内向怎么办 学生做试卷马虎怎么办 小孩子学习态度不好怎么办 孩子学习态度差怎么办