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":   "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的使用,以及如何由地址读取网络图片。

 @Overrideprotected 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 intsubStrDuration = 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 urlpublic 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally{try {is.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return null;}

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

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


监听网络变化

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

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

    class NetworkChangeReceiver extends BroadcastReceiver {@Overridepublic 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工程源码

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

  1. Android Video Player. 安卓视频播放器,封装 MediaPlayer、ExoPlayer、IjkPlayer。模仿抖音,悬浮播放,广告播放,列表播放,弹幕

    DKVideoPlayer 项目地址:dueeeke/DKVideoPlayer 简介: Android Video Player. 安卓视频播放器,封装 MediaPlayer.ExoPlayer. ...

  2. android vr播放器 开发,Android应用开发之Android VR Player(全景视频播放器)- ExoPlayer播放器MPEG-DASH视频播放...

    本文将带你了解Android应用开发之Android VR Player(全景视频播放器)- ExoPlayer播放器MPEG-DASH视频播放,希望本文对大家学Android有所帮助. Androi ...

  3. android 全景播放器,Android VR Player(全景視頻播放器) [5]:簡單的歡迎界面

    Android VR Player(全景視頻播放器) [5]:簡單的歡迎界面 歡迎界面 在繼續下一部分,即視頻列表實現的介紹前,分享一下簡單的歡迎界面的實現.一來是可以整合一下前面說的側滑菜單和底部導 ...

  4. Android VR Player(全景视频播放器) [6]:视频列表的实现-本地视频

    Android VR Player(全景视频播放器) [6]:视频列表的实现-本地视频 (本篇博客参考<Android第一行代码(第二版)>中关于RecyclerView的部分) 列表的实 ...

  5. Android 全景视频播放器(VR视频播放器探索二)

        上次随便写着玩的  http://blog.csdn.net/ai_yong_jie/article/details/51159367   Android 全景视频播放器(VR视频播放器探索一 ...

  6. 基于Google的Android平台上GVR 3D全景视频播放器(支持本地文件和视频流传输)

    基于GVR(Google VR)安卓平台下的 3D全景视频播放器 Google GVR GVR简介 示例应用 源码实现 GVR关键的api调用 Gradle配置 效果图 布局 m3u8和hls协议(自 ...

  7. android 播放视频链接,如何通过Android视频播放器中的直接链接播放MP4视频?

    我正在制作一个 Android应用程序,我需要通过直接下载链接在Android默认本机视频播放器中播放mp4视频. 要打开Android视频播放器,我使用以下代码 Intent intent = ne ...

  8. 全景视频播放器中OpenGL的相关记录

    全景视频播放器中OpenGL的相关记录 一.OpenGL顶点数组 二.坐标系与投影 三.坐标系相关函数 四.纹理坐标 五.纹理过滤 六.深度缓冲区 七.OpenGL的glut库 OpenGL函数功能g ...

  9. android阿里云基础视频播放器

    android阿里云基础视频播放器记录: 1.阿里云视频播放器sdk地址:https://help.aliyun.com/document_detail/61910.html?spm=a2c4g.11 ...

最新文章

  1. c语言switch不允许实型,C语言中switch语句什么意思
  2. keras从入门到放弃(三)逻辑回归 softmax
  3. OpenSSL 编程 - RSA 加密解密
  4. 20个Flutter实例视频教程-第03节: 不规则底部工具栏制作-1
  5. java查询SQLServer遇到问题:对象名无效。
  6. 电脑格式化后需要重装系统吗_重装系统后c盘文件丢失,电脑重装系统后c盘文件能恢复吗...
  7. 1036 和奥巴马一起学编程
  8. Linux(一)之Ubuntu上安装nginx、nginx-rtmp-modeule、libx264、FFmpeg(巨详细)
  9. 一位硕士毕业生三个月求职经历与经验的结晶
  10. getParameterValues和getParameter的区别
  11. 电脑播放视频显示运行服务器失败,电脑不能播放视频如何解决?
  12. Linux介绍及安装过程 常用的命令 对目录或文件的增删改查 压缩和解压缩 用户和权限 shell/shellScript linux的一些其他命令
  13. 记录:2018年CCF优秀博士学位论文奖信息
  14. python点云可视化工具_点云生成鸟瞰图(Python)
  15. 《风之旅人》游戏设计思想二
  16. 线性代数回顾(多视图重建)
  17. @Transactional子事务单独提交
  18. Python 命名关键字形参
  19. 基于MPS先进CFD软件Prometech.ParticleWorks.6.0.0.161003
  20. 日本人为何能拿这么多诺贝尔奖

热门文章

  1. AlphaControls 控件 动画
  2. Jconsole简介
  3. 融优学堂生物演化13.5
  4. 开源项目在线化 中文繁简体转换/敏感词/拼音/分词/汉字相似度/markdown 目录
  5. 本人的QQ群:有来的可以加。
  6. 解决espressif-ide启动失败报错Incompatible JVM问题
  7. 公司企业中说的KT是什么意思?
  8. 为什么学不会平面设计?初学者该如何入门?
  9. Python设计代码,检测密码强度
  10. 毕业设计 U-Net遥感图像语义分割(源码+论文)