这次在安卓上通过网络请求实现一个哔哩哔哩用户视频获取软件以及获取 GitHub 用户 Repos 和 Issues 的应用,主要使用到的库有RxJava
、Retrofit2
以及okHttp
。
实现内容
实现一个 bilibili 的用户视频信息获取软件
- 搜索框只允许正整数 int 类型,不符合的需要弹 Toast 提示
- 当手机处于飞行模式或关闭 wifi 和移动数据的网络连接时,需要弹 Toast 提示
- 由于 bilibili 的 API 返回状态有很多,这次我们特别的限制在以下几点
- 基础信息 API 接口为:
https://space.bilibili.com/ajax/top/showTop?mid=<user_id>
- 图片信息 API 接口为基础信息 API 返回的 URL,cover 字段
- 只针对前 40 的用户 id 进行处理,即
user_id <= 40
- [2,7,10,19,20,24,32]都存在数据,需要正确显示
- 基础信息 API 接口为:
- 在图片加载出来前需要有一个加载条,不要求与加载进度同步
- 布局和样式没有强制要求,只需要展示图片/播放数/评论/时长/创建时间/标题/简介的内容即可,可以自由发挥
- 布局需要使用到 CardView 和 RecyclerView
- 每个 item 最少使用 2 个 CardView,布局怎样好看可以自由发挥,不发挥也行
- 不完成加分项的同学可以不显示 SeekBar
- 输入框以及按钮需要一直处于顶部
实现一个 github 用户 repos 以及 issues 应用
- 教程位于
./manual/tutorial_retrofit.md
- 每次点击搜索按钮都会清空上次搜索结果再进行新一轮的搜索
- 获取 repos 时需要处理以下异常:HTTP 404 以及 用户没有任何 repo
- 只显示 has_issues = true 的 repo(即 fork 的他人的项目不会显示)
- repo 显示的样式自由发挥,显示的内容可以自由增加(不能减少)
- repo 的 item 可以点击跳转至下一界面
- 该 repo 不存在任何 issue 时需要弹 Toast 提示
- 不完成加分项的同学只需要显示所有 issues 即可,样式自由发挥,内容可以增
应用截图
- BiliBili
应用主界面 | 正常搜索 |
---|---|
拖动进度条 |
- Github
首页 | 搜索 Repo |
---|---|
添加 Issue | 添加成功 |
没有 Issue |
界面
主界面由一个EditText
、MaterialButton
和RecyclerView
组成,使用ConstraintLayout
进行布局,输入框和按钮在顶部,而列表则填充剩下的部分。
列表中的布局由两个MaterialCardView
组成,第一个卡片显示图片、标题、进度条等信息,第二个卡片显示主要内容。
在使用MaterialCardView
之前,还需要先设置好依赖implementation 'com.android.support:design:28.0.0'
这些界面的布局都是非常基本的东西,简单地写一下就可以了,这里就不详细说明了。
网络请求
这里使用了HttpURLConnection
和RxJava
实现网络请求。
首先声明我们需要的两个 API
1 | private final String baseURI = "https://space.bilibili.com/ajax/top/showTop?mid=%d"; |
然后编写一个根据用户 ID 获取基本信息的接口
HttpsURLConnection
这个使用起来十分简单,只需要使用 URL 的openConnection
方法,就可以获取一个HttpsURLConnection
然后设置好一系列的参数(如 Get 方法、其实可以不用)
最后调用getInputStream
方法就可以获得这次请求的回应数据主体。
使用Gson.fromJson
方法就可以将回应的Json
转化为一个类
1 | try { |
RxJava
因为网络请求是异步的,因此我们可以使用RxJava
来优雅地实现异步请求。
首先将上面的方法封装一下,返回一个Observable
对象,在得到结果的时候调用mitter.onNext(baseInfoObj)
,发生错误的时候调用emitter.onError(e)
1 | Observable<BaseInfoObj> getBaseInfo(final int id) { |
然后写一个Observer
观察者,订阅这个网络事件,当数据返回的时候,就可以在Observer
里面的onNext
或者onError
处理。
1 | Observer<BaseInfoObj> baseInfoObserver = new Observer<BaseInfoObj>() { |
而加载图片的请求和上面的基本差不多,也是使用RxJava
调用
1 | // Load Image |
检测网络连接
这里使用ConnectivityManager
来获取网络状态
1 | private boolean isNetworkConnected() { |
进度条
拖动进度条可以显示预览的图片,首先我们要获取预览的数据和图片。
我这里把他们放在了Adapter
的ViewHolder
进行bind
的时候实现。
链式请求
这里涉及到两个连续的请求,就是需要先获取数据,然后根据数据中的地址获取图片。
这里就可以用到RxJava
的flatMap
实现
1 | httpRequest.getPVideo(obj.getData().getAid()) |
这里看上去只有一个请求,而实际上是一个链式请求。
首先通过getPVideo
获取 Json 信息,然后使用flatMap
,当上一个请求的onNext
被调用,结果就会被传递到apply
函数里,在其apply
函数中再次发起网络请求,并且将结果传递给下一个Observer
。
两个请求可以使用RxJava
合二为一,链式调用完成。
图片切割
当进度条拖动的时候,就更新预览图,而预览图是一张 100 张图合成的大图。
首先计算出当前进度条说对应的图片的索引,然后根据 API 获取到的图片的信息(每行多少块、每列多少块、每块多长、每块多高),找出图片中预览图的起始x
和y
以及height
和width
最后使用Bitmap.createBitmap(pvImages.get(page),x,y,width,height)
方法切割图片并且显示
1 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
当释放进度条时候,需要重置为原来的图片
1 |
|
Retrofit2 & okHttp
在 Github 请求部分,用到了Retrofit2
和okHttp
声明 API
Retrofit2 使用起来非常简单,他是基于注释实现 RESTful 风格的 API 请求,只需要写一个简单的接口,就可以实现请求。
1 | public interface GitHubService { |
使用注释表明请求方法GET
或者POST
,然后还可以使用Headers
给请求加上请求头
请求中动态变化的部分,使用{}
标识出来,然后再参数中使用Path
表明这个参数的值
对于POST
请求,可以使用Body
标识请求体
声明返回结构
对于返回的结果,需要声明一个类来解析
1 | public class IssueItem { |
为了保持变量的驼峰命名法,可以使用@SerializedName
表示序列化时候所对应的变量名
使用方法
1 | OkHttpClient client = new OkHttpClient.Builder() |
使用OkHttp
承载Retrofit
的请求,只需要声明一些参数然后使用.client()
传给Retrofit
即可
然后使用create
创建服务
因为这里用了RxJava
,因此返回的是Observable
,和上面提到的用法一样,指定观察者就可以接收这次请求的数据,然后再做出处理
1 | service.listRepos(username).observeOn(AndroidSchedulers.mainThread()) |
POST 请求
POST 请求和 Get 有些不同,对于自定义的类型,需要提供一个结构体
1 | JSONObject result = new JSONObject(); |
新建一个JSONObject
对象,然后把数据体(Issue 的标题和内容)通过put
方法传进去,然后使用RequestBody
创建一个application/vnd.github.symmetra-preview+json
类型的请求对象
然后调用Retrofit
就可以发起 POST 请求了
总结
这次实验运用了网络请求,虽然说在期中项目上我们已经使用过Retrofit
和LiveData
实现网络请求和数据库请求的结合,但是这次实验也进一步加深了对一些有认证的请求的用法。Github
的 API 就是一套非常标准的Restful API
非常适合我们进行学习。