您的位置:首页 > 理论基础 > 计算机网络

Android VR Player(全景视频播放器) [7]:视频列表的实现-网络视频

2017-06-04 23:04 686 查看

Android VR Player(全景视频播放器) [7]:视频列表的实现-网络视频

前期准备

在之前的博文,Android VR Player(全景视频播放器) [6]:视频列表的实现-本地视频 中, 和大家分享了如何使用AsnycTask来实现读取本地媒体库中的视频信息,并用RecyclerView来实现以列表的方式进行展示。本篇博文以本地视频列表的这篇博文为基础,继续和大家分享如何实现“网络视频列表”。

这里需要说明一下为什么每篇博文的代码都是分开的,而不是一步步迭代,最后得到一个完整的播放器的。因为自己也是刚开始Android开发,水平还很low,还不敢写“跟着我一步步做全景视频播放器”这样的博客。写这个系列的目的也主要是想分享自己在这个全景视频播放器实现过程中遇到的一些问题的解决思路,方法,希望能给正好有这方面需要的同学一点点帮助。每部分单独分开,也可以方便大家“各取所需”。

读取网络视频列表

老朋友:doInBackground

前一篇博客中已近和大家分享过异步任务AsyncTask类的使用方法,我们把读取本地媒体库这个耗时的任务放在了doInBackground方法中。要实现网络列表,就要读取网络视频列表信息,而这个也是一个耗时的任务,也是要放到doInBackground中的。那么我们的网络视频列表信息从何而来,又怎么读取呢?这就涉及到数据传输的格式的选择和Android网络流的相关操作了。

读取JSON数据

在之前的博客 Linux ffmpeg视频截图,C中操作JSON数据 中我分享了如何在C中操作JSON数据,顺便在那篇博客里说了自己对于JSON数据的一些理解,有兴趣的同学可以去看看,这里就不再重复说明JSON的格式等内容,而是直接介绍如何在Android中解析服务器上的JSON数据。

既然谈到服务器,首先我们得有个服务器啊!为了方便,我们直接在本地用MyEclipse创建一个web项目TestOnlineList,然后把准备好的JSON数据和视频缩略图一起放到项目的WebRoot目录下。如果你有远程的服务器的话,也可以将JSON文件和其他一些必要的文件放到web服务器的目录下。

我们准备的视频列表的JSON文件为:

{
"status":   1,
"data": [{
"name": "dubai.mp4",
"videoThumb":   "http://10.0.2.2:8080/TestOnlineList/dubai.mp4.jpg",
"duration": "00:01:20.60",
"createTime":   "2017-5-9 13:34",
"path": "http://10.0.2.2:8080/TestOnlineList/dubai.mp4"
}, {
"name": "360test.mp4",
"videoThumb":   "http://10.0.2.2:8080/TestOnlineList/360test.mp4.jpg",
"duration": "00:00:42.63",
"createTime":   "2017-5-9 14:1",
"path": "http://10.0.2.2:8080/TestOnlineList/360test.mp4"
}, {
"name": "panda.mp4",
"videoThumb<
f19b
/span>":   "http://10.0.2.2:8080/TestOnlineList/panda.mp4.jpg",
"duration": "00:01:56.82",
"createTime":   "2017-5-9 14:1",
"path": "http://10.0.2.2:8080/TestOnlineList/panda.mp4"
}],
"msg":  "SUCCESS"
}


包含三个视频的信息,一个视频对象有name,videoThumb(存放的视频截图的地址),duration等键值对。然后把这个项目添加MyEclipse自带的Tomcat服务器中,并启动服务器。现在我们的服务器端就准备好了,再来看Android端。

大家应该很容易想到,要读取服务器数据,总得有个地址吧,但是这个地址应该怎么写呢?

private static String jsonURL = "http://10.0.2.2:8080/TestOnlineList/videolist.json";


10.0.2.2是一个A类的局域网地址,用来表示本地局域网,我们在Android模拟器中需要使用这个地址来访问本地局域网,注意不要用127.0.0.1。Tomcat默认的端口为8080,所以我们还需要加上该端口,后面是我们的项目名,之后是json文件名。地址也有了,下一步就是读取和解析。

先读取,再解析。读取其实就是从服务器拿到json数据流,并把它转成String;解析则是把这个String按照JSON的组织格式把对象信息给提取出来。我们创建一个readStream方法,它用来读取输入流,返回String对象。

private String readStream(InputStream is) {
InputStreamReader isReader;
String result = "";
String line = "";
try {
isReader = new InputStreamReader(is, "utf-8");
BufferedReader buffReader = new BufferedReader(isReader);
while ((line = buffReader.readLine()) != null) {
result += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return result;
}


完整的 doInBackground方法的代码如下,但是我们只需留意其中的部分代码:如何获得JSONString,JSONObject的使用, JSONArray的使用,以及如何由地址读取网络图片。

@Override
protected Void doInBackground(Void... params) {
try {
String jsonString = readStream(new URL(jsonURL).openStream());

String path = "";
String name = "";
String createdTime = "";
String strDuration = "";
int duration2second = 0;
boolean isLocal  = FALSE;
String imageUrl = "";

JSONObject jsonObject;
jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("data");

Log.d(TAG, "doInBackground: jsonArray:"+ jsonArray.toString());

for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);

imageUrl = jsonObject.getString("videoThumb");
name = jsonObject.getString("name");//
strDuration = jsonObject.getString("duration");
String subStrDuration = "";
try {
//change xx:xx:xx to int
subStrDuration = strDuration.substring(0,8);
duration2second = 0;
} catch (NumberFormatException e) {
e.printStackTrace();
}
createdTime = jsonObject.getString("createTime");
path = jsonObject.getString("path");

VideoItem data = new VideoItem(path, name, createdTime,duration2second,subStrDuration,isLocal,imageUrl);
data.createThumb();
publishProgress(data);
mDataList.add(data);
}//for
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}


JSONString使用我们之前的readStream即可获取。JsonObject,也就是JSON对象,JSONArray也就是JSON数组,JSON数组由JSON对象组成。先用

jsonObject = new JSONObject(jsonString);


把从网络流中读到的jsonString中的JSON对象解析出来。在我们服务器的videolist.json中,视频信息对象数组的名字是“data”,于是我们用JSONObject的getJSONArray方法,获得JSON数组。

JSONArray jsonArray = jsonObject.getJSONArray("data");


大家可能很迷糊了,一会儿对象,一会儿数组的,难道不应该是先把数组解析出来,再一个个得到数组中的对象吗?为什么是用JSONObject的getJSONArray方法去获得数组呢?简单地说,JSON中,{}括起来的是对象,[]括起来的是数组,所以我们先用new JSONObject(jsonString)获取的对象是包含了data这个数组的一个对象,然后再从这个对象中用getJSONArray去获得JSON数组。所以会看到,在循环 for (int i = 0; i < jsonArray.length(); i++) 中,我们有 jsonArray.getJSONObject(i),这时获取的对象显然就是JSON数组中的对象了。

可能有点绕,不过这点清楚了的话,后面就没什么难题了,针对每个数组中的每个object,用getString(“key”)去获取key对应的value就可以了。然后再把获得的值给VideoItem对应的属性。

我们把每个视频的缩略图的地址也通过JSON传输过来了,要在列表中展示缩略图就涉及到如何从得到的图片地址去读取网络图片。我们在VideoItem中写了一个getBitmapFromUrl,

//get online video thumb by url
public Bitmap getBitmapFromUrl(String urlString) {
InputStream is = null;
Bitmap bitmap;
try {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(connection.getInputStream());
bitmap = BitmapFactory.decodeStream(is);
connection.disconnect();
return bitmap;
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally
{
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}


主要用到HttpURLConnection来获取网络输入流,并利用工厂方法BitmapFactory.decodeStream来解析输入流,获得bitmap。

准备好需要的数据后,具体怎么展示到RecyclerView中就和本地视频列表一样了。

监听网络变化

有的时候我们的网络并不是可用的,这时就没法加载我们的网络视频列表,我们需要在网络不可用时给用户相应的反馈,比如弹出个Toast,提示“当前网络不可用”。

监听网络变化需要用到Android的广播机制,广播接收器和前一篇博客中提到的内容提供器一样,是Android四大组件之一。

class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
ConnectivityManager mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if(mNetworkInfo != null && mNetworkInfo.isAvailable()){
Log.d(TAG, "onReceive: NETWORK AVAILABLE");
}else{
Toast.makeText(context,"network is unavailable",Toast.LENGTH_SHORT).show();
}
}
}


我们在MainActivity中创建一个内部类,NetworkChangeReceiver,它继承自 BroadcastReceiver,然后我们重写了onReceive方法,并且在网络不可用时,Toast一条消息“network is unavailable”。代码很简单,方法名,类名都很规范,所以应该很容易看懂其中的意思。根据《第一行代码》书中讲的(没有实践验证,大家有兴趣可以试一下),因为广播接收器中不允许开启线程,如果onReceive方法很久都没有结束的话,程序会报错,所以尽量只在onReceive中执行耗时较短的简单的代码。

然后在MainActivity的onCreate方法中动态地注册网络变化监听,当然也可以静态地在Androidmanifest中注册,两者的区别是动态方式更加灵活,但必须要程序启动后才能开始监听,而静态的方式在程序未启动时也能接收到广播。

IntentFilter mIntentFilter = new IntentFilter();
mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
NetworkChangeReceiver mNetworkChangeReceiver = new NetworkChangeReceiver();
this.registerReceiver(mNetworkChangeReceiver,mIntentFilter);


mIntentFilter用来指定我们意图的动作是监听“CONNECTIVITY_CHANGE”(连通变化,或者连接变化),实例化一个NetworkChangeReceiver,然后动态地注册这个监听。注意别忘了在MainActivity中onDestroy使用

this.unregisterReceiver(mNetworkChangeReceiver);


来注销监听。

需要注意的是,我们就算在模拟器上开了飞行模式,还是可以正常访问到本地服务器的数据的,但网络监听还是会提示网络状态不可用,真正使用外网服务器时,网络不可用的情况下是不能读取到网络服务器数据的。

最后是权限问题,在Androidmanifest中添加以下权限,第一个权限是用来访问网络状态,第二个权限是访问WIFI网络状态(后期网络视频播放时会用到,比如用户在非WIFI模式下播放网络视频,可以提供相应的提示),第三个权限是网络访问权限,这是我们访问网络服务器数据时需要的。这几个权限都不是敏感权限,所以静态注册即可。

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


与本地视频列表的整合

可以看到,本地视频列表的实现和网络视频列表的实现其实没有太大差异,只是数据的获取方式不同,一个读本地,一个读网络。所以,如果要做一个包含本地和网络视频列表的app时,VideoItem.java,VideoItemAdapter.java,videoitem.xml等等都是可以用同一个的,只是需要做一些判断。比如在VideoItem的构造函数中传入指定这个video是网络视频还是本地视频,然后在VideoItem中分别针对网络和本地视频提供相应的方法来生成视频缩略图。

网络视频列表的解析,视频缩略图的创建等都是比较耗时的操作,再联系前面WelcomeActivity的作用,我们就可以把这部分比较耗时的操作放在WelcomeActivity中,需要用的时候直接拿过来用就可以了,用户就不用进入app后再等待加载资源,从而提升了用户体验。

至此,全景视频播放器大致的UI设计已经完成,基本有了 Android VR Player(全景视频播放器) [1]:项目介绍 中展示的效果。“千米长跑”,我们已经跑完了头一百米,开了个好头,后面就是更大的挑战。视频播放控制,OpenGL ES的使用,网络视频压缩传输,服务器搭建等等都比界面设计更加复杂,更加困难。下一篇博客会分享一下Android中视频播放相关的问题,主要内容是一个简单的视频播放控制的实现。

测试结果



网络视频列表的展示效果



网络状态监听,可以看到开了飞行模式的情况下,弹出网络不可用的提示“network is unavailable”,说明网络监听监听生效了。需要注意的是,因为我们使用的是本地局域网,所以没有网络情况下仍然是可以加载本地tomcat服务器上的视频列表数据的。

Reference

Android之JSON格式数据解析

Android异步加载访问网络图片-解析json

Linux ffmpeg视频截图,C中操作JSON数据

JSON的三种解析方式

参考源码

链接: https://pan.baidu.com/s/1i4S83hN 密码: qfcu

源码中service压缩包为web工程源码,Android压缩包为Android工程源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: