实验目的
- 学会使用HttpURLConnection请求访问Web服务
- 学习Android线程机制,学会线程更新UI
- 学会解析JSON数据
- 学习CardView布局技术
效果

添加依赖
1 | //json解析类 |
本例使用的
RxJava是2.2.4版本,与1.X版本有较大区别!!!
添加权限
在app/src/main/AndroidManifest.xml中添加
1 | <uses-permission android:name="android.permission.INTERNET"/> |
这次的网络访问权限不用动态申请啦!!!
但是
高版本(Android 8以上)的SDK可能会报这样的错误
Cleartext HTTP traffic to xxx not permitted
Google表示,为保证用户数据和设备的安全,针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输
这里采用最简单的办法
在app/src/main/AndroidManifest.xml中的application中添加
1 | android:usesCleartextTraffic="true" |
强行使用明文传输
更多办法:https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted
使用HttpURLConnection获取网络数据
示例API网址: https://space.bilibili.com/ajax/top/showTop?mid=2
1 | URL url = new URL(baseURL + "showTop?mid=" + editText.getText().toString()); |
这样子就可以通过byteArrayOutputStream.toString()得到字符串
不过字符串是JSON类型数据,需要解析才能方便使用
JSON解析类
观察网页返回的json结果
1 | {"status":true,"data":{"aid":349,"state":0,"cover":"http:\/\/i2.hdslb.com\/bfs\/archive\/8d15f47650e6e95e11ad7a3d6ae06ea52231169a.jpg","title":"[\u4eba\u751f\u7684\u5bfc\u5e08\u677e\u5188\u4fee\u9020]\u6771\u65b9\u4fee\u5922\u9020","content":"sm5159640","play":186049,"duration":"01:23","video_review":936,"create":"2009-09-08 17:00:16","rec":"e'm'm'm'm'm","count":1}} |
根据json中的属性新建实体类RecyclerObj.java
实体类中不要求实现所有的属性
1 | import android.graphics.Bitmap; |
后来就可以使用new Gson().fromJson(byteArrayOutputStream.toString(), RecyclerObj.class)将
String转换为RecyclerObj对象了
还有个实体类PreviewObj.java用来获取预览图的链接返回的JSON数据
1 | package com.sysu.janking.httpapi; |
结合RxJava
但是像上面一样直接运行的话会报NetworkOnMainThreadException的异常,因为网络访问不能在主线程中进行,容易造成阻塞或者UI瘫痪
在MainActivity.java中添加被观察者
1 | private CompositeDisposable mCompositeDisposable = new CompositeDisposable(); |
在搜索按钮的点击事件中添加观察者
1 | DisposableObserver<RecyclerObj> disposableObserver = new DisposableObserver<RecyclerObj>() { |
其实如果JSON返回的不一定就是上述定义好的结构
如输入一个不存在的用户名,就会出现以下另一种结构的JSON数据
1 | {"status":false,"data":"\u53c2\u6570\u9519\u8bef"} |
跟上述的区别在于,
错误返回data字段只有一个String类型的数据
而正确返回data字段是另一个JSON数据(对应一个实体类)
所以当gson解析JSON时在data后面找不到{,便会抛出com.google.gson.JsonSyntaxException异常
所以可以通过判断这个异常间接判断用户名是否存在数据
而通过
recyclerObj.getStatus()这个结果返回false判断用户名不存在的语句理论上一直不回执行,因为如果用户名不存在的话一定没有data字段的JSON数据,所以一定会抛出异常,不能成功解析出status字段
通过RecyclerView显示结果
布局文件recycler_item.xml

布局中使用了cardview,上面是一个ImageView(用来显示视频封面)和一个Progressbar(显示加载进度),但是此时ImageView是隐藏的(visibility = “gone”),
因为进度条和封面不能同时显示,当封面加载完成后进度条会隐藏
中间有一个SeekBar,通过拖动显示视频的预览,但是默认是不可拖动的(在java文件里设置),只有预览图加载成功后才可以拖动
下面是一些TextView显示简介、播放数、评论数、时长、创建事件等
1 |
|
实体类RecyclerObj.java
直接复用Json实体类就好了就好了
适配器RecyclerAdapter
1 | public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> { |
Adapter确实干了不少事!
首先对于RecyclerView里的每个项设置内容,然后对于数据中的aid,
在RxJava中用另一个”https://api.bilibili.com/"`api`链接获取预览图的`JSON`数据
得到数据之后, 在RxJava中另开一个线程得到JSON中的URL指向的图片,转换为bitmap,并存储到ArrayList中;而且还通过RecyclerObj中的cover指向的URL得到封面图存储到bitmap变量中
下面是对Adapter的补充
设置图片宽度占满屏幕
1 | //获取屏幕宽度 |
不过要在xml里设置ImageView的android:adjustViewBounds="true"
通过时间字符串得到毫秒数
1 | String total = mData.get(position).getIdata().getDuration(); |
new SimpleDateFormat("mm:ss").parse(total)得到的结果是缺省1970年1月1日00:00:00
如输入字符串是"01:23",指定格式为"mm:ss"
得到的结果是
Thu Jan 01 00:01:23 GMT+08:00 1970
因此要得到需要的时间的毫秒数(没有得到秒数的方法……)只有减去另一个基准值如"00:00"
SeekBar的拖动事件
1 | holder.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { |
因为没有解析出每张预览图的时间点,所以就粗略的把预览图当做整个视频上平均分配的时间点,通过这句把一定时长上的SeekBar值转换为一定数量预览图的某一张
progress * (holder.preview_bitmaps.size()-1) / holder.seekBar.getMax()
通过url得到bitmap文件
1 | //通过url得到bitmap文件 |
裁剪bitmap图片
1 | public static ArrayList<Bitmap> cutBitmap(Bitmap bitmap, int img_x_len, int total_len, int img_x_size, int img_y_size){ |
MainActivity.java中绑定Adapter
1 | //about RecyclerView |