Retrofit2 简明教程(一)

Retrofit2 简明教程(一)

相信大家都听过Retrofit的大名但是没有实际运用,或是已经运用过Retrofit1.x,因为Retrofit1.x和Retrofit2.x差别非常大,Retrofit1.x教程也是非常多,为了简单易懂,所以本文将以最新Retrofit2实践运用满足我们的Retrofit日常开发,后续我们也会更深入的了解Retrofit2,最后在本文中的尾页将附上Demo。

在阅读过程中有任何问题,请及时联系。如需转载请注明 fuchenxuan de blog

  • Retrofit2 简明教程一
  • 简介
  • 安装
    • SOURCE
    • GRADLE推荐
    • MAVEN
    • PROGUARD
  • 最初的步骤
    • 创建Retrofit实例
    • 更换HTTP client与Converter
  • 基本使用
    • GET

      • 普通GET
      • 动态参数GET
      • RESTful方式动态PATH
    • POST
      • form-data表单数据
      • JSON参数raw
      • RESTful方式动态PATH
    • 文件上传
    • 大文件下载
  • 注解
  • 更多的Retrofit内容
    • CallAdapter
  • 附件

简介

Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 。Retrofit 可以利用接口,方法和注解参数来声明式定义一个请求应该如何被创建。并且可更换或自定义HTTP client,以及可更换或自定义Converter,返回数据解析方式。Retrofit可用于Android和Java的一个类型安全(type-safe)的REST客户端,如果你的服务器使用的使RESTAPI,那么你将非常适合使用它。

安装

请选择以下三种方式中一种进行安装,最后如果你正在使用PROGUARD,请添加下方PROGUARD配置。

SOURCE

关于Retrofit源代码以及官方简单例子,请访问http://github.com/square/retrofit

GRADLE(推荐)

如果你正在使用GRADLE在你的项目中的build.gradle添加以下代码到您的配置:

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

MAVEN

如果你正在使用MAVEN在你的项目中的pom.xml添加以下代码到您的配置:

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>retrofit</artifactId>
  <version>2.1.0</version>
</dependency>

PROGUARD

如果你正在使用PROGUARD在你的项目中添加以下代码到您的配置:

-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions

最初的步骤

创建Retrofit实例

在使用Retrofit前,我们需要先创建Retrofit实例,并且做一系列配置,然而Retrofit设计的也是非常好,这些配置都是可插拔的:

      Retrofit retrofit = new Retrofit.Builder()
                //设置baseUrl,注意baseUrl 应该以/ 结尾。
                .baseUrl("http://news-at.zhihu.com/api/4/")
                //使用Gson解析器,可以替换其他的解析器
                .addConverterFactory(GsonConverterFactory.create())
                //设置OKHttpClient,如果不设置会提供一个默认的
                .client(new OkHttpClient())
//                .client(new UrlConnectionClient())
//                .client(new ApacheClient())
//                .client(new CustomClient())
                .build();

更换HTTP client与Converter

Retrofit 背后的 HTTP client,以及序列化机制(JSON/XML 协议)等都是可以替换,因此你可以选择自己合适的方案。Retrofit 最早出来的时候,只支持 Apache 的 HTTP client。后来增加了 URL connection,以及 OkHttp 的支持。如果你想使用其他的 HTTP client,可以通过以下方式了替换,或者更改为自定义的HTTP client:

 //设置OKHttpClient,如果不设置会提供一个默认的OkHttpClient
                .client(new OkHttpClient())
//                .client(new UrlConnectionClient())
//                .client(new ApacheClient())
//                .client(new CustomClient())

序列化功能也是可替换的。默认是用的 GSON,你当然也可以用 Jackson 来替换掉。

    //使用Gson解析器,可以替换其他的解析器
    .addConverterFactory(GsonConverterFactory.create())
    //当需要返回原始String数据时
    .addConverterFactory(ScalarsConverterFactory.create())

除此之外Retrofit还提供以下几种Converter:

  • 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,andString): com.squareup.retrofit2:converter-scalars

基本使用

我们将看一下如何用Retrofit与服务器交互,通过它你将学会如何运用Retrofit于日常开发。

Retrofit使用接口,方法和参数,使用注解表明了请求将如何处理,每一种方法都必须有一个HTTP标注提供请求的方法和相对URL,有五种内置注解:GET, POST, PUT, DELETE, 和 HEAD,在注解中指定URL。请选择以下方式中合适的请求方式来处理您的请求。

GET

在这里我们最开始第一个GET请求使用的是知乎日报的api,为了更好使用Retrofit其他请求方式而又没有比较好的公开api,我自行编写了配合使用Retrofit的测试服务端,放置在外网以便大家测试使用。

普通GET

基于上面的最初的步骤,接下来我们需要定义一个接口,并且使用注解(@GET)表明一次GET请求:

public interface ZhihuService {

    //获取启动页大图
    @GET("start-image/1080*1776")
    Call<StartImageBean> getStartImage();
}

这是一个普通的GET请求,接着我们来看如何利用Retrofit 创建服务接口,并且设置参数:

        ZhihuService messageService = retrofit.create(ZhihuService.class);
        Call<StartImageBean> startImage = messageService.getStartImage();

最后,使用startImage.enqueue进行异步请求,并且获取了我们期待的数据(实体对象):

 startImage.enqueue(new Callback<StartImageBean>() {

            @Override
            public void onResponse(Call<StartImageBean> call, Response<StartImageBean> response) {

                if (response.isSuccessful()) {
                    Log.d(TAG, response.body().toString());
                    resultTextView.setText("" + response.body().toString());
                }

            }

            @Override
            public void onFailure(Call<StartImageBean> call, Throwable t) {
                resultTextView.setText("" + "error:" + t.getMessage());
            }
        });

如果你想使用call.execute()进行同步请求,需要注意的是不要放在UI线程:

try{
    Response<StartImageBean> response = call.execute(); // 同步
    Log.d(TAG, "response:" + response.body().toString());
} catch (IOException e) {
    e.printStackTrace();
}

因为一次call.execute() 的request只能执行一次,否则你将会得到如下错误:

java.lang.IllegalStateException: Already executed

如果你想取消本次请求可以使用 startImage.cancel()或者是复制一次request,再次请求:

 startImage.cancel();//取消
 Call<StartImageBean> cloneRequsest = startImage.clone();//复制

是不是感觉特别简单,使用时候只需调用接口,这一切都简化了我们的操作。

动态参数(GET)

我们需要先定义一个接口,并且使用注解(@Query)或者是@QueryMap表明动态参数请求如何处理:

相应的URL是这样:

http://baseurl/app/test/sayHello?username=fuchenxuan&age=110

    @GET("test/sayHello")
    Call<String> sayHello(@Query("username") String username, @Query("age") String age);

接着我们忽略接口的创建,直接使用Retrofit与服务器交互,值得注意的是我们此次返回的数据是String,而不是一个自定义的实体类对象。所以我们需要更换Converter,否则你将会遇到不必要的麻烦(而我觉得Retrofit应该提供一个默认Stirng的实现):

    //当需要返回原始String数据时
    .addConverterFactory(ScalarsConverterFactory.create())

接着我们就得到了我们期待的数据:

Call<String> doubanCall = myTestApiService.sayHello("fuchenxuan", "110");
        doubanCall.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

                if (response.isSuccessful()) {
                    Log.d(TAG, response.body().toString());
                    resultTextView.setText("" + response.body().toString());
                }

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });

RESTful方式(动态PATH)

GET 动态PATH 就是优雅的RESTful api方式,

相应的URL是这样:

http://news-at.zhihu.com/api/4/start-image/1024*782

@GET("start-image/{size}")
    Call<StartImageBean> getStartImageByPath(@Path("size") String size);

POST

form-data(表单数据)

form-data 就是如表单K-V参数形式

这里其实就跟GET的动态参数是一致的只是替换了@POST注解

public interface MyTestApiService {

@POST("test/sayHello")
    Call<ResultBean> postSayHello(@Query("username") String username, @Query("age") String age);

}

JSON参数(raw)

当服务器需要你POST 参数以json打包数据格式请求时,然而这种参数方式RESTful api 也是非常常见的,我们需要使用@Body注解:

 @POST("test/sayHi")
   // @Headers("Accept-Encoding: application/json")
        //使用@Headers 可添加header
    Call<ResultBean> postSayHi(@Body UserBean userBean);

上面我们还示例了如何使用@Headers@Headers("Accept-Encoding: application/json")添加头部信息,或者我们有需求需要使用@Header实现动态头部信息:

 @POST("test/sayHi")
    @Headers("Accept-Encoding: application/json")
        //也可以使用@Header 可添加header
    Call<ResultBean> postSayHi(@Body UserBean userBean, @Header("city") String city);

RESTful方式(动态PATH)

前面也说了retrofit非常适用于RESTful url的格式,这里因为知乎的就是RESTful api,我们直接使用和GET动态URL一样的注解(@PATH)来表明请求处理:

相应的URL是这样:

http://news-at.zhihu.com/api/4/start-image/1024*782

@POST("start-image/{size}")
    Call<StartImageBean> getStartImageByPath(@Path("size") String size);

文件上传

在我们开发当中肯定必不可少图片上传了,我们使用表单上传文件时,必须让 <form> 表单的 enctyped 等于 multipart/form-data

文件上传我们需要使用@MultiPart@Part ,MultiPart意思就是允许多个@Part多部分上传。

@Multipart
    @POST("test/upload")
    Call<ResultBean> upload(@Part("file\"; filename=\"launcher_icon.png") RequestBody file);

值得注意的是我们需要在@Part指定file和filename的值,避免一些不必要的麻烦。

相应的我们在使用retrofit的时候,首先先获取到文件,并且创建RequestBody实例,然后调用接口请求,相应代码块如下:

File file = new File(getExternalFilesDir(null), "launcher_icon.png");
        RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);
        Call<ResultBean> doubanCall = myTestApiService.upload(fileBody);

        doubanCall.enqueue(new Callback<ResultBean>() {
            @Override
            public void onResponse(Call<ResultBean> call, Response<ResultBean> response) {

                if (response.isSuccessful()) {
                    Log.d(TAG, response.body().toString());
                    resultTextView.setText("" + response.body().toString());
                }

            }

            @Override
            public void onFailure(Call<ResultBean> call, Throwable t) {
//                Log.d(TAG, response.body().toString());
                resultTextView.setText("" + "error:" + t.getMessage());
            }
        });

大文件下载

文件下载我们需要使用@Url@Streaming@Url动态Url正好非常适合我们的场景,而使用@Streaming注解可以让我们下载非常大的文件时,避免Retrofit将整个文件读进内存,否则可能造成OOM现象。

声明接口如下:

    @Streaming
    @GET
    Call<ResponseBody> downloadFileByDynamicUrlAsync(@Url String downloadUrl);

需要注意的是我们需要使用Retrofitcall.execute同步获取ResponseBody,那么我们就需要放进一个单独的工作线程中:

new AsyncTask<Void, Long, Void>() {

         @Override
            protected Void doInBackground(Void... voids) {
                Call<ResponseBody> call = myTestApiService.downloadFileByDynamicUrlAsync(API_BASE_URL.concat("/res/atom-amd64.deb"));
              try {
                    Response<ResponseBody> response = call.execute();
                    boolean writtenToDisk = writeResponseBodyToDisk(response.body());
                    Log.d(TAG, "下载文件 " + writtenToDisk);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
            }
        }.execute();

最后我们需要将文件写入磁盘根目录中:

//写入到磁盘根目录
    private boolean writeResponseBodyToDisk(ResponseBody body) {
        try {
            File futureStudioIconFile = new File(Environment.getExternalStorageDirectory() + File.separator + "atom.deb");
            InputStream inputStream = null;
            OutputStream outputStream = null;

            try {
                byte[] fileReader = new byte[4096];

                final long fileSize = body.contentLength();
                long fileSizeDownloaded = 0;

                inputStream = body.byteStream();
                outputStream = new FileOutputStream(futureStudioIconFile);
                while (true) {
                    int read = inputStream.read(fileReader);

                    if (read == -1) {
                        break;
                    }

                    outputStream.write(fileReader, 0, read);

                    fileSizeDownloaded += read;

                    Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);

                    final long finalFileSizeDownloaded = fileSizeDownloaded;
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            resultTextView.setText("file download: " + finalFileSizeDownloaded + " of " + fileSize);
                        }
                    });
                }

                outputStream.flush();

                return true;
            } catch (IOException e) {
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }

                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            return false;
        }
    }

这样我们就可以非常高效的下载大文件了,最后友情提醒(如果是6.0以上另外再申请权限):

 <uses-permission android:name="android.permission.INTERNET" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

注解

尽管文章前面已经把Retrofit2注解基本了解完了,但是有必要多了解一些其他的注解,或许您正好有这样的场景需求,他们分别可作用于方法和参数:

  • @Headers:用于在方法添加请求头:
    @POST("test/sayHi")
    @Headers("Accept-Encoding: application/json")
    Call<ResultBean> postSayHi(@Body UserBean userBean, @Header("city") String city);
  • @Streaming

    如果您正在下载一个大文件,Retrofit2将尝试将整个文件移动到内存中。为了避免这种,我们必须向请求声明中添加一个特殊的注解@Streaming

@Streaming
@GET
Call<ResponseBody> downloadFileByDynamicUrlAsync(@Url String fileUrl);
  • @Header :用于在方法参数里动态添加请求头:
Call<ResultBean> postSayHi(@Header("city") String city);
  • @Body :用于Body的JSON格式参数
 @POST("test/sayHi")
 Call<ResultBean> postSayHi(@Body UserBean userBean);
  • @Url

    使用动态的请求的网址,会复写之前的baseUrl,值得注意的是@Url需要在所有参数之前:

    @POST
    Call<ResultBean> postSayHelloByURL(@Url String url,@Query("username") String username, @Query("age") String age);
  • @Path

    主要适合用于RESTful api ,动态URL,比如https://baseUrl/api/v1/,将API的版本号放入URL,变换Path切换不同版本的API。

@POST("/api/{version}/{size}")
    Call<StartImageBean> getStartImageByPath(@Path("size") String size,@Path("version") String version);

以下几个都是用于服务器接受表单参数类型(form-data)时使用

  • @Filed
  • @FiledMap

    这两个需要和@FormUrlEncoded配合使用,参数形式体现在请求体上:

    @FormUrlEncoded
    @POST("test/sayHello")
    Call<ResultBean> postSayHelloByForm(@Field("username") String username, @Field("age") String age);
  • @Query
  • @QueryMap

    这两个和@Filed@FiledMap功能是一致的,区别在于参数形式体现在URL上,类似这样:http://baseurl/app/test/sayHello?username=fuchenxuan&age=110的URL

    @GET("test/sayHello")
    Call<String> sayHello(@Query("username") String username, @Query("age") String age);
  • @Part
  • @PartMap

    这两个用于上传文件,与@MultiPart注解结合使用

    @Multipart
    @POST("test/upload")
    Call<ResultBean> upload(@Part("file\"; filename=\"launcher_icon.png") RequestBody file);

更多的Retrofit内容

如果你已经完全读完了这篇文章并且也实践着编写了这个程序,那么你一定已经能够非常熟练自如地使用Retrofit2了,你可能也已经编写了一些Retrofit2程序来尝试练习各种Retrofit2特性。如果你还没有那样做的话,那么你一定要快点去实践。

本文末尾将略带一下Retrofit2其他特性,后续文章我们也会有更伟大的实践。

CallAdapter

Retrofit2提供了三个CallAdapter:

  • guava:com.squareup.retrofit2:adapter-guava
  • Java8:com.squareup.retrofit2:adapter-java8
  • rxjava:com.squareup.retrofit2:adapter-rxjava

    你肯定很期待将和与Java8 或者是rxjava结合使用,当然你也可以自定义CallAdapter,满足自己的需求。那么我们将会在下篇文章中一起学习这个快乐的编程。

附件

Retrofit2 使用Demo.zip

Retrofit2 简明教程(一)就到这了,文章中的示例代码在下面,如有问题欢迎在下方留言,及时让我知道,写一篇好的技术blog好难,但您的支持是我的动力,希望您在下方的留言让我做的更好。

作者:fuchenxuan

出处:http://blog.csdn.net/vfush

欢迎访问我的个人站点:http://fuchenxuan.cn

转载请注明出处–http://blog.csdn.net/vfush

时间: 2024-10-31 19:30:04

Retrofit2 简明教程(一)的相关文章

Lisp简明教程

此教程是我花了一点时间和功夫整理出来的,希望能够帮到喜欢Lisp(Common Lisp)的朋友们.本人排版很烂还望多多海涵! <Lisp简明教程>PDF格式下载 <Lisp简明教程>ODT格式下载 具体的内容我已经编辑好了,想下载的朋友可以用上面的链接.本人水平有限,如有疏漏还望之处(要是有谁帮我排排版就好了)还望指出!资料虽然是我整理的,但都是网友的智慧,如果有人需要转载,请至少保留其中的“鸣谢”页(如果能有我就更好了:-)). Lisp简明教程 整理人:Chaobs 邮箱:[

Linux防火墙iptables简明教程

前几天微魔部落再次遭受到个别别有用心的攻击者的攻击,顺便给自己充个电,复习了一下linux下常见的防火墙iptables的一些内容,但是无奈网上的很多教程都较为繁琐,本着简明化学习的目的,微魔为大家剔除了许多冗余的内容,提取出尽量多的精华部分成文,和大家共同学习,本文涉及的内容包括如下 Linux防火墙iptables简明教程 1.安装iptables 2.查看现有的iptables规则 3.删除某iptables规则 4.清除现有iptables规则 5.创建规则 6.设置开机启动 7.保存i

Markdown简明教程4-Markdown UML图

1. 前言 Markdown是一种轻量级的标记语言,把作者从繁杂的排版工作中解放出来,实现易读易写的文章写作,已经逐渐成为事实上的行业标准.CSDN博客支持Markdown可以让广大博友更加专注于博客内容,大赞.但是,不少博友可能对Markdown比较生疏,本博接下来用一个系列文章<Markdown简明教程>扼要介绍Markdown,希望可以对大家有所帮助. 系列教程目录 关于Markdown Markdown基本使用 Markdown表格和公式 Markdown UML图 CSDN Mark

JSP 简明教程(二):JSP基本语法

基本语法 JSP只是在html的基础上嵌入一些动态的元素.下面是HelloWorld代码: <html> <% out.println("hello world"); %> </html> 以上代码中的<% %>就是动态元素.JSP中所有的特殊语法如下: <% %>:用于表示一段Java代码.这段代码在编译之后会放在_jspService()方法中. <%! %>:多了一个叹号,也用于表示一段Java代码,但是这段

第一课 C语言简明教程

1序言: 1与Java.C#等高级语言相比,C语言却非常简单,学习简单,使用也简单,但是也非常重要,到目前为止基本上操作系统的内核代码超过百分之九十使用C语言完成,因此学好C语言是学好计算机这门课程的基础,特别是进入系统编程尤为明显. 今天是本人复习C语言课程的第一课,主要重新记录一下C语言的基础知识,这节课涉及到C语言的结构.变量以及类型.输入输出.条件判断以及循环知识. 2知识点: 2.1 C语言的结构 2.1.1 通常情况下C语言程序是由: 1.相关的代码注释,使用/* ··· */可注释

Vbs 脚本编程简明教程之一

-为什么要使用 Vbs ? 在 Windows 中,学习计算机操作也许很简单,但是很多计算机工作是重复性劳动,例如你每周也许需要对一些计算机文件进行复制.粘贴.改名.删除,也许你每天启动 计算机第一件事情就是打开 WORD ,切换到你喜爱的输入法进行文本编辑,同时还要播放优美的音乐给工作创造一个舒心的环境,当然也有可能你经常需要对文本中的某 些数据进行整理,把各式各样的数据按照某种规则排列起来--.这些事情重复.琐碎,使人容易疲劳. 第三方软件也许可以强化计算机的某些功能,但是解决这些重复劳动往

Smarty教程1.引擎定义2.主要优点3.简明教程4.使用判断5.循环数组6.常见问题8.解释程序

Smarty是一个php模板引擎.更准确的说,它分开了逻辑程序和外在的内容,提供了一种易于管理的方法.可以描述为应用程序员和美工扮演了不同的角色,因为在大多数情况下 ,他们不可能是同一个人.例如,你正在创建一个用于浏览新闻的网页,新闻标题,标签栏,作者和内容等都是内容要素,他们并不包含应该怎样去呈现.在Smarty的程序里,这些被忽略了.模板设计者们编辑模板,组合使用html标签和模板标签去格式化这些要素的输出(html表格,背景色,字体大小,样式表,等等).有一天程序员想要改变文章检索的方式(

《Python简明教程》总结

Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 面向对象 输入输出 异常 标准库 更多的内容

Java泛型简明教程

Java泛型简明教程 博客分类: Java综合 JavaApple数据结构CC++ Java泛型简明教程 本文是从 Java Generics Quick Tutorial 这篇文章翻译而来. 泛型是Java SE 5.0中引入的一项特征,自从这项语言特征出现多年来,我相信,几乎所有的Java程序员不仅听说过,而且使用过它.关于Java泛型的教程,免费的,不免费的,有很多.我遇到的最好的教材有: The Java Tutorial Java Generics and Collections ,