最近,笔者对安卓开发的兴趣愈发浓厚,而且不断尝试了许多传闻很棒的开发库 -- 大部分也真的很不错。于是打算写一个系列文章,介绍使用这些让人惊叹的库建立安卓示例应用的实践。这样,读者可以自行判断,这些库有多好用。本文是该系列的第一篇。

今天,笔者要研究对应用来说,使用 Retrofit 2 的 HTTP 客户端有多复杂,以及能带来什么好处。Retrofit是Square Inc.发布在开源社区的一款令人叹为观止的工具。它是一个类型安全的 HTTP 客户端,适用于安卓和 Java 应用。

类型安全 HTTP 客户端主要意味着,你只需关心发送出去的网络请求的语义,而不必考虑 URL 构建的细节,以及参数设定是否正确之类的事。Retrofit应对这些易如反掌,你只需写几个接口即可。就是这么简单!

我们通过一个例子来看看它是怎么实现的。笔者已经把这里所有的代码都放在了 Github 的 资源库里。如你所知,最好的学习方法就是查看这些代码,自己调试调试。

首先,笔者在 Android Studio 里建了一个空项目。老实说,每次看到欢迎界面上出现的那堆有关兼容性、以及应用所支持的 API 的选项,笔者都会感到非常迷茫。



好在我们只是弄个“玩具”项目而已,因此我们可以大胆地选择最新的 SDK,然后快速掠过项目向导,进入我们伟大的「hello world」安卓应用。

如果你自己不想纠结这些选项,可以直接点击这个 Github 的链接,导入笔者提交的现成项目:Floating button app skeleton.

通常,笔者在所有项目中都会启用 JRebel for Android。这是一个 Android Studio 的插件。“启用它”基本上意味着,只需点击一下自定义按钮就能运行应用,其余所有工作都交给它了。JRebel for Android 对安卓开发者来说是一款提升效率的工具,它可以在运行着的设备和模拟器上即时更新代码。这本质上意味着在开发应用时,你不必浪费宝贵的时间来等待应用重启,并因此而中途放弃这些应用的状态记录。总之特别棒!

说明一下,我在 ZeoTurnaround 公司工作,也就是创造JRebel for Android的公司。但它真的很好用,你值得一试。

Android Studio 2.0附带的新模拟器用着也很爽。现在,MainActivity 类的模拟器屏幕被激活了。

点一下浮动按钮,出现 snackbar。看起来不错。让我们随意更改一段代码,检验一下代码重新加载的功能。笔者首先想到的是修改 snackbar 的字符串值。

  1. FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
  2. fab.setOnClickListener(new View.OnClickListener() {
  3. @Override
  4. public void onClick(View view) {
  5. Snackbar.make(view, Super fast hello world”, Snackbar.LENGTH_LONG)
  6. .setAction(“Action”, null).show();
  7. }
  8. });

注意看,我们保存了一个文件,点击了一下按钮,接着模拟器上的代码就重新加载了。现在一切就绪!也就是说,我们可以研究 Retrofit 2 到底有什么用处了。

Retrofit 2 是一个类型安全的 HTTP 客户端,适用于安卓(及 Java)。但首先,它也是一个库。所以我们要先声明依赖。这个容易。不过,请注意,我们需要显式依赖gson 转换器,将 JSON 应答转换为 model 类。这与Retrofit 1不同。所以需要注意一下。

Add these two lines to the build.gradle file:

将这两行代码加到 build.gradle 文件中去:

  1. compile com.squareup.retrofit2:retrofit:2.0.0-beta2
  2. compile com.squareup.retrofit2:gson-converter:2.0.0-beta2

Retrofit 的主要功能是可以在运行时生成代码,发送 HTTP服务查询请求。开发者只需写一个“说明”接口即可。假设我们有如下的一个 model 类:

通过这个我们可以创建一个名为GithubService的接口,用于实现 HTTP 通信。


  1. public interface GitHubService {
  2. @GET(“repos/{owner}/{repo}/contributors”)
  3. Call<List<Contributor>> repoContributors(
  4. @Path(“owner”) String owner,
  5. @Path(“repo”) String repo);
  6. }

This is the simplest example, we add the @GET annotation on an interface method and provide the path part of the URL that we want to expose it on. Conveniently, the method parameters can be referenced in the path string so you won’t need to jump through hoops to set those. Additionally, with other annotations you can specify query parameters, POST request body and so on:

  • @Query(“key”) — for GET request query parameter
  • @QueryMap — for the map of parameters
  • @Body — use it with the @POST annotation to provide the query body content.

这是一个最简单的例子。我们在接口方法中加入 @GET 注解,并且提供想要展示的 URL 的path部分。方便的是,由于可以在 path 字符串中引用方法参数,因此我们无需跳出循环再重新设定。另外,通过这些注解,你可以设定查询参数,以及 POST 请求主体等等:

  • @Query(“key”)  -- 用于GET请求查询参数
  • @QueryMap -- 用于参数映射
  • @Body -- 与@POST注解一起使用,提供查询主体内容

下面,为了能在运行时使用这个接口,我们需要构建一个 Retrofit 对象:


  1. interface GitHubService {
  2. @GET("repos/{owner}/{repo}/contributors")
  3. Call<List<Contributor>> repoContributors(
  4. @Path("owner") String owner,
  5. @Path("repo") String repo);
  6. public static final Retrofit retrofit = new Retrofit.Builder()
  7. .baseUrl("https://api.github.com/")
  8. .addConverterFactory(GsonConverterFactory.create())
  9. .build();
  10. }

笔者倾向于在包含网站请求的接口内使用 Retrofit builder。这么做并不是想让它变得复杂难懂。而是处于通用配置的考虑。例如,我们有默认的转换器,将 JSON 响应对象转为 Java 对象,但是,即使在每个 service 中复制粘贴一遍,也好过使用单一的抽象类,后者很容易造成泄漏。

With these pieces in place, we just need to perform the network call:

  • the specification of our queries
  • the Retrofit object builder

    准备好这些代码后,我们只需发出网络请求:
  • 请求的说明
  • Retrofit对象builder

为了实现GitHubService接口,需要初始化一个用于执行 HTTP 查询请求的 Call 对象。


  1. GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
  2. Call<List<Contributor>> call = gitHubService.repoContributors(“square”, retrofit”);
  3. List<Contributor> result = call.execute().body();

另外,有人可能也会选择设定请求的时间,让它成为异步请求,同时在执行完毕后提供 callback。


  1. call.enqueue(new Callback<List<Contributor>>() {
  2. @Override
  3. public void onResponse(Response<List<Contributor>> response, Retrofit retrofit) {
  4. // handle success
  5. }
  6. @Override
  7. public void onFailure(Throwable t) {
  8. // handle failure
  9. }
  10. });

听起来超简单!我们来处理一下 UI,然后写入代码。在浮动按钮的应用模板下,我们需要修改 content_main.xml 文件。笔者添加了一个发起请求查询的按钮,以及一个用以显示请求结果的文本区域:


  1. <Button
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content"
  4. android:text="Fetch"
  5. android:id="@+id/button"
  6. android:layout_alignParentBottom="true"
  7. android:layout_centerHorizontal="true"
  8. android:layout_marginBottom="151dp" />
  9. <TextView
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:textAppearance="?android:attr/textAppearanceLarge"
  13. android:text=""
  14. android:id="@+id/textView"
  15. android:layout_above="@+id/button"
  16. android:layout_alignParentEnd="true"
  17. android:layout_alignParentStart="true"
  18. android:layout_alignParentTop="true"
  19. android:textIsSelectable="false" />

第一次执行时,你的网络请求代码看起来可能是这样的:


  1. Button button = (Button) findViewById(R.id.button);
  2. button.setOnClickListener(new View.OnClickListener() {
  3. @Override
  4. public void onClick(View view) {
  5. GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
  6. Call<List<Contributor>> call = gitHubService.repoContributors(“square”, retrofit”);
  7. String result = call.execute().body().toString();
  8. TextView textView = (TextView) findViewById(R.id.textView);
  9. textView.setText(result);
  10. }
  11. });

这段代码自然无法运行。安卓框架不允许用户在 UI 线程中执行网络请求。UI 线程只应该用来处理一些用户输入。在这个线程中执行任何引起长时间阻塞的操作都会让用户体验变得非常糟糕。

因此,我们需要重构这段代码,把网络请求移入后台线程。使用 JRebel for Android 可以不费任何时间就搞定这事。我们将代码提取到 AsyncTask -- 这是一个在安卓上运行大型运算的默认框架。AsyncTask不太好看,它的具体运行方式也不算清洁,例如每次我们点击按钮时它都会去创建一个 Retrofit 对象。但是能用就行。


  1. private class NetworkCall extends AsyncTask<Call, Void, String> {
  2. @Override
  3. protected String doInBackground(Call params) {
  4. try {
  5. Call<List<Contributor>> call = params[0];
  6. Response<List<Contributor>> response = call.execute();
  7. return response.body().toString();
  8. } catch (IOException e) {
  9. e.printStackTrace();
  10. }
  11. return null;
  12. }
  13. @Override
  14. protected void onPostExecute(String result) {
  15. final TextView textView = (TextView) findViewById(R.id.textView);
  16. textView.setText(result);
  17. }
  18. }

接着,从 EventListener 中调用它:

  1. Button button = (Button) findViewById(R.id.button);
  2. button.setOnClickListener(new View.OnClickListener() {
  3. @Override
  4. public void onClick(View view) {
  5. GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
  6. final Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");
  7. new NetworkCall().execute(call);
  8. }
  9. });

好啦,现在代码可以运行了。文本视图会根据 HTTP 请求的结果实时刷新。

现在,这个框架应用程序已经完成了,代码顺利构建并运行完毕。你可以自己去试试 Retrofit和JRebel for Android 。随意修改一些代码,检查一下新代码构成的应用程序运行效果如何,也不会浪费任何时间。尝试为 Contributor 类增加几个区域,替换 contributor 组件的 文本视图,更改 HTTP 的端点,将请求发往另一个站点。一切尽在你掌握!

在本文中,我们了解了如何使用 Retrofit 2 创建一个发送网络请求的简易框架应用程序。程序的代码可以在 Github 上找到。对本文的最佳使用方式是,将它看作一篇快速指导,根据你的侧重点写一段自己的代码,接着复制这个应用程序的代码,导入 Android studio,然后试试你自己的代码吧!

今后,笔者会扩展这个框架应用程序,加入一些依赖注入框架,把UI代码与其他代码区分开来,以及使用RxAndroid来响应式处理一些网络请求等等。另外,你在安卓项目中默认使用的库是哪一个?研究一下这个问题也许也会很有意思呢!在Twitter上联系我:@shelajev。期待与你们互动!

OneAPM Mobile Insight以真实用户体验为度量标准进行 Crash 分析,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客

本文转自 OneAPM 官方博客

如何用 Retrofit 2 在安卓上实现 HTTP 访问?的更多相关文章

  1. Android Retrofit 2.0文件上传

    Android Retrofit 实现(图文上传)文字(参数)和多张图片一起上传 使用Retrofit进行文件上传,肯定离不开Part & PartMap. public interface ...

  2. UNITY2018.3 在editor下运行时new memoryprofiler显示 shader占用内存很大的问题在安卓上并没有看到

    在安卓机上完全看不到shader占用的内存,但问题是,shader在安卓上真的几乎不占用内存了?(我们的游戏中只使用了mobile下的shader) 参考下面这个文章,说是真机上也有: Unity3D ...

  3. 安卓上为什么不能用system.io.file读取streammingAssets目录下的文件

    首先,看文档: Streaming Assets   Most assets in Unity are combined into the project when it is built. Howe ...

  4. lighttpd + php for android 安卓上的WEB服务器

    lighttpd + php for android 安卓上的WEBSER 这个项目在 http://hex.ro/wp/blog/php-and-lighttpd-for-android 目前不支持 ...

  5. [RN] React Native中使用 react-native-scrollable-tab-view嵌套在ScrollView里,导致 子内容 在安卓上无法显示

    React Native中使用 react-native-scrollable-tab-view嵌套在ScrollView里,导致 子内容 在安卓上无法显示 问题: 0.9.0 或 0.8.0 版本的 ...

  6. Android | 教你如何在安卓上实现通用卡证识别,一键各种卡绑定

    目录 前言 通用卡证识别的应用场景 如何使用通用卡证识别服务 集成通用卡证识别服务的关键流程 开发实战 1 开发准备 1.1 在项目级gradle里添加华为maven仓 1.2 在应用级的build. ...

  7. java 使用cookie记录用户上一次访问的时间 记住 用户的 登录名

    package cn.itcast.cookie; import java.io.IOException; import java.io.PrintWriter; import java.util.D ...

  8. hadoop的API对HDFS上的文件访问

    这篇文章主要介绍了使用hadoop的API对HDFS上的文件访问,其中包括上传文件到HDFS上.从HDFS上下载文件和删除HDFS上的文件,需要的朋友可以参考下hdfs文件操作操作示例,包括上传文件到 ...

  9. 事物的隔离级别与并发完美体现了cap理论(确保数据完整、安全、一致性,在此基础上实现高性能访问(鱼和熊掌不可兼得)

    事物的隔离级别与并发完美体现了cap理论(确保数据完整.安全.一致性,在此基础上实现高性能访问(鱼和熊掌不可兼得)

随机推荐

  1. nyoj 86 找球号(一)

    找球号(一) 时间限制:3000 ms  |            内存限制:65535 KB 难度:3   描述 在某一国度里流行着一种游戏.游戏规则为:在一堆球中,每个球上都有一个整数编号i(0& ...

  2. HW-IP合法性_Java

    描述 现在IPV4下用一个32位无符号整数来表示,一般用点分方式来显示,点将IP地址分成4个部分,每个部分为8位,表示成一个无符号整数(因此不需要用正号出现),如10.137.17.1,是我们非常熟悉 ...

  3. Jsp与servlet之间页面跳转及参数传递实例(转)

    原网址:http://blog.csdn.net/ssy_shandong/article/details/9328985 11. jsp与servlet之间页面跳转及参数传递实例 分类: Java ...

  4. java星座、年龄、日期等

    星座: public static String getStar(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(dat ...

  5. Flexbox介绍

    CSS3 弹性盒( Flexible Box 或 flexbox),是一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式.对于很多应用来讲,弹性盒改进了盒模型,既不使用浮动 ...

  6. java集合 collection-list-LinkedList 模拟一个堆栈或者队列数据结构。

    /* 使用LinkedList模拟一个堆栈或者队列数据结构. 堆栈:先进后出 如同一个杯子. 队列:先进先出 First in First out FIFO 如同一个水管. */ import jav ...

  7. Java_log4j

      Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.事件记录器等:我们也可以控制每一条日志的输出格式: ...

  8. MyBatis入门教程(基于Mybatis3.2)

    MyBatis和Hibernate一样都是基于ORM的关系型数据库框架 ORM工具的基本思想: 1.从配置文件(通常是XML配置文件中)得到 sessionfactory. 2. 由sessionfa ...

  9. C#语法需要注意的地方

    笔试中遇到的一些C#语法方面的问题,由于平时很少用到,笔试的时候模棱两可,现在做一个笔记. using System; using System.Collections.Generic; using ...

  10. Web前端新人之CSS样式选择器

    最近在学习css样式.那么我就想先整理一下css样式的选择器 规则结构: 每个规则都有两个基本部分:选择器和声明块.声明块由一个或者多个声明组成,每个声明则是一个属性—值对(property-valu ...