现在我们制作一个真正的 API 调用,证明我们的抽象 RxJava真实有效.之后在 App 中来显示一些真实的数据.

全部章节:

Kotlin — Part 0:关于这个系列
Kotlin — Part 1:配置 Android Studio
Kotlin — Part 2:语法,空安全,静态类型
Kotlin — Part 3:扩展函数、Android 扩展、委托属性
Kotlin — Part 4:RecyclerView— Kotlin 适配器委托&数据类
Kotlin — Part 5:Kotlin,RxJava&RxAndroid
Kotlin — Part 6:API-Retrofit&Kotlin)
Kotlin — Part 7:无限滑动:高阶函数& Lambdas
Kotlin — Part 8:方向改变(序列化&数据类)
Kotlin — Part 9:单元测试与 Kotlin(Mockito,RxJava)

Github 仓库:https://github.com/imuhao/KedditBySteps


我们先不实现无线滑动,我们将只查看最新的10个新闻.

Reddit API

让我们复习一下我们需要从 Reddit 加载的 API.我们的想法是从 Reddit 加载Top 新闻到我们的App 中.

请求 URL

我们需要去请求的 URL:

1
www.reddit.com/top.json?limit=10

你可以粘贴到你的浏览器,查看到像这样:

你可以查看到返回的数据是一个 JSON 格式,一个最好的方法区分析这个内容是医用一个 Json 解析,像 Json Editor Online,这是一个优秀的在线工具.


这里有3个事情我们需要考虑的:

  • childer:这是一个10个分类列表,每一个 children 是关于新闻的详情
  • after:它允许你执行分页操作.” after”将使你得到下面10个items, 通过调用:”/top.json?after=t3_7b7zfo”
  • befor:这是和” after”类似的运行你执行返回操作

一个 children 包含许多的信息.我们只是挑选一些我们需要的.

Retrofit

它是一个 Android 和 Java 中类型安全的 HTTP 客户端

Retrofit 是一个非常简单的第三方扩展,允许我们将 HTTP API 请求转变成一个 Java(Kotlin)结构.我们使用它来消费我们的 Reddit Api.

依赖

我们需要添加这个 Retrofit 依赖

1
2
compile "com.squareup.retrofit2:retrofit:2.0.0"
compile "com.squareup.retrofit2:converter-moshi:2.0.0"

API 模型: Kotlin 类

我们需要一些类来转换我们的 json 相应到Kotlin 类中.如果使用 Java 我们需要创建一个很大的类文件,但是我们将使用 Kotlin, 我们可以取得优势,使类非常简单.简单到在这里我可以粘贴出这个类的所有代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RedditNewsResponse(val data: RedditDataResponse)
class RedditDataResponse(
val children: List<RedditChildrenResponse>,
val after: String?,
val before: String?
)
class RedditChildrenResponse(val data: RedditNewsDataResponse)
class RedditNewsDataResponse(
val author: String,
val title: String,
val num_comments: Int,
val created: Long,
val thumbnail: String,
val url: String
)

API 接口

我创建了一个 Kotlin 文件 RedditApi, 它看起来像这样:

1
2
3
4
5
interface RedditApi {
@GET("/top.json")
fun getTop(@Query("after") after: String, @Query("limit") limit: String)
: Call<RedditNewsResponse>
}

我们在这里定义个一个同步的 API 请求,它接受一个” after”和” limit”查询字符串.返回类型是” Call“, 这个 Call 允许我们执行请求,也可以知道这个请求是否成功,也将响应的数据转换成指定的类型.

Rest API

现在我们将在一个新的” RestApi.kt”文件中初始化 Retrofit.在 Kotlin 中我们使用 init 关键字初始化代码.

1
2
3
4
5
6
7
8
9
10
class RestApi {
private val redditApi: RedditApi
init {
val retrofit = Retrofit.Builder()
.baseUrl("https://www.reddit.com")
.addConverterFactory(MoshiConverterFactory.create())
.build()
redditApi = retrofit.create(RedditApi::class.java)
}
}
  1. init代码块: Kotlin 类的初始化代码块
  2. “.addConverterFactory(MoshiConverterFactory.create())”设置 Moshi 转换
  3. “RedditApi::class.java”这个语法运行你得到与 Java Class 实例相似的 KClass实例

getNews

我们也提供一个函数来消费新闻API

1
2
3
fun getNews(after: String, limit: String): Call<RedditNewsResponse>{
return redditApi.getTop(after, limit)
}

重要:

确认添加这个权限到AndroidManifes 文件

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

NewsManager:调用这个 Api!!

我们使用默认参数来初始化我们的 RestApi:

1
2
3
class NewsManager(private val api: RestAPI = RestAPI()) {
...
}

更新我们的 Observable 来调用这个 Api 执行请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
subscriber ->
val callResponse = api.getNews("", limit)
val response = callResponse.execute()
if (response.isSuccessful) {
val news = response.body().data.children.map {
val item = it.data
RedditNewsItem(item.author, item.title, item.num_comments,
item.created, item.thumbnail, item.url)
}
subscriber.onNext(news)
subscriber.onCompleted()
} else {
subscriber.onError(Throwable(response.message()))
}
  1. callResponse.execute():这个将执行一个同步请求
  2. resoinse.body().data.children.map{…}:这里我们使用的 List 函数” map” 将每一个子类转换成一个 RedditNewsItem
  3. it.data:it 是一个小的方法来访问 lambda 中单个参数.这个只有当你只有一个参数时有效.如果有多个参数你需要使用这种方式“x, y -> …”.
  4. subscriber.onError():我们创建了一个新的 Throwable 对象来接受服务端的错误消息.

Commit

https://github.com/imuhao/KedditBySteps/commit/40d48484d400c173beae458864e62f828a87e243

我们的 app 现在从Reddit 获取10个新闻

总结

很好,现在我们越来越接近最终的 APP. Retrofit 是一个非常强大的扩展,可以肯定的是我们只使用了它的一部分功能.
在下一个部分我们将添加无限滑动从 Reddit得到更多的新闻.