转载自:http://geek.csdn.net/news/detail/98643
React Native是近年来最值得花时间学习的移动开发新技术,其在不断进化、成熟的同时,性能也在持续提升。卜赫主要分享了React Native Pili在开发过程中的设计斟酌和踩过的坑。以下是正文:
React Native是什么
React Native的iOS和Android版本目前是由官方在维护,Windows版本是微软做了迁移,淘宝有把React Native移植到Web上,另外还有工程师已经把它移植到了OS X上了。所以在将来如果你会React Native,就会很容易成为一个全端工程师。
React Native用的是ReactJS语法。其他的跨平台方案一般是先编译成中间码再在虚拟机中运行,例如Java和.Net,React Native却不太一样,因为跨平台是非常难的,平台视图会有很大差异。React Native的思路是让我们学习这种开发模式后可以用这套模式去开发任何平台的应用,即所谓的「learn once,write anywhere」。但是开发的代码会稍微有些不同,例如在Web端会写一些HTML的Tag,在React Native中则会写一些抽象出来的原生组件的Tag。
React Native和原有的Hybrid框架有何区别呢?首先,它是用原生组件去渲染的,而不是用WebView,后面的优势劣势也是因此产生的。其次,它的响应速度也不像是在手机浏览器里面跑的WebApp,它所有的页面都是Native。另外,它的动画也不是CSS模拟的,而是一些原生动画,这个则可以给用户带来非常好的体验。
当然Hybrid框架也有一些优势,比如你可以用你想用的所有前端库,并且也确实做到了跨平台,但是随着React Native的不断进步——几乎支持所有的平台,这个优势正在慢慢地被蚕食。
React Native本身是一个UI框架,它更多的像一个库。原来前端可能都是大的吓人,需要把通讯、MVC等全部解决了,而React Native只是一个UI,只需用它来写界面,其他的问题你自己去解决即可。你选择哪种架构,React Native并不关心,它只专注地把UI部分做好。当然,这也会带来一些问题,因为所有的东西都是视图,做的时候会带来一些挑战。
直播简介
知道了React Native后,我们来了解下直播。了解直播前,让我们先理性地认识一下什么是视频。视频大家每天都看,但是到底什么是视频呢?其实它是一个封装,主要封装了三部分内容,如图1所示。上面的部分是Metadata,下面是声音,中间最主要的是图片。它是用一些连续的图片来欺骗我们的眼睛,当超过每秒24帧的时候,我们看起来就会是连续的。
图 2
直播的模型比较简单,如图2所示。由主播发起,推送流到云端,然后其他的人通过搜索来观看直播。直播的协议有很多,比较主流的有RTMP、HLS。RTMP协议是Adobe公司开发的,HLS则是苹果开发的,它们的主要区别如图3所示。
图 3
其中,RTMP是用的TCP长连接,所以它是一个长连接协议。而HLS从本质上来讲,是一个播放列表,持续不断地更新这个播放列表,最终会产生一些ts的切片文件。直接用苹果的浏览器打开之后就可以看HLS,有些Android也支持,但是HLS的延时比较长,超过10秒钟。
图 4
同时,它们具体的使用场景也有一些不同,如图4所示。在即时互动的情况下,我们倾向于选用RTMP协议,例如在秀场应用中,如果一个土豪给主播发了一个红包,但是主播10秒之后才互动就会很不好。而广播协议HLS,则比较适用于做点播和回放,例如我们在看一个演唱会的时候,可能所有人在看这个演唱会都是延迟了10秒钟,但是你不会关心这10秒钟,因为每一秒都延时了10秒钟。
一个直播应用需要多大开发量
相信映客、龙珠直播等直播APP大家都有用过,但是你是否知道一个直播应用需要多大开发量呢?图5、图6是我们开发的一个最简单的直播应用的核心代码展示。
图 5
图 6
我们会发现,一个直播应用核心开发竟然只需2行代码。前面我们知道,直播模型就是一个推流和收流。图5是推流的代码,里面可以设置推到云端的地址、推流的分辨率、码率率等属性,设置完后就可以推流,推流以后就可以播放了,播放的时候还可以设置播放的地址、硬件码、软件码等属性,如图6。
上面的那些代码为何如此简单,里面都隐藏了哪些细节呢?其实我们在背后做了很多工作,而且用了不止一种语言。React Native是JS语言,它是跑在iOS和Android上的。但是我们在做的过程中,并没有用JS做视频的编解码,也没有用JS去实现RTMP的协议,主要原因是一些性能问题无法解决。最终采用的方案是JS+Objective-C+Java,即在Android里使用JS+Java,在iOS里面使用Objective-C,然后在最上层用一个统一的API。对应用层工程师而言是跨平台的,即你在Android和iOS里面写的代码是完全一样的,但是底层我们会用Java和Objective-C做一定的适配工作。以某个APP为例,图7是推流部分,最上面的Streaming是用JS来写的,下面性能关键的部分是用Objective-C来写的,比如推流有一个预览的视图,有一个协议的推流的管理器,然后还有一个编码器。播放部分与推流类似,如图8所示。上面是一个Preview的JS适配在Android的方案,下面基本是用Native来写的,有一个播放页面和播放流的管理器,包括是硬解码还是软解码。
图 7
图 8
所以总结下来,不管在底层的React Native有多少的状态,我们最终都能通过JS和Native Code把它们隐藏在所有的细节里,最终展现给开发者看的只有一个最终页面和够用的属性,即Preview View和Player View。
API 的设计与调用
React Native是以View为中心的,所以在设计React Native API的时候,强制要求我们API的设计风格和访问方式统一化,主要有「视图」和「属性」这两个概念,这无疑对API设计者的功力要求更高了。
首先来看布局管理,布局管理其实是告诉大家这个界面是什么样的,目前React Native支持如下一些属性,如图9所示。
从经验来看,这些属性基本上可以满足绝大部分的开发需求了,你可以尽量做一些弹性的布局、按比例的布局,因为你可以拿到设备的整个层高宽度和高度,进而做一些百分比布局。
图 9
然后是配置管理,如图10所示。所谓的配置管理,就是那些一旦设定就不需要改变的东西,因为你的界面一旦确定了,你是要推哪些流和码率,这些在整个推流过程中是不变的。因此,这个配置是相对比较静态的配置。
图10
后面是状态管理,如图11所示。状态显然与配置不一样,因为它是可以变的,例如用户可以切换是否静音或画面大小缩放比例等。
图 11
接着是动作管理,如图12所示。这部分可能与过去的设计有一些差异,之前在设计API的时候,动作一般会用方法来解决。这里实际也是一个状态,因为你的API不会触发底层的状态改变。但是它是反过来的,是先声明目前的状态,然后再根据当前的状态去实际的调离底层的方法,是开始推流还是停止推流。
图 12
图13是事件管理,与原生的API也不同。这里可以是一个本地函数或者闭包,当某个事件过来的时候可以调用你的函数。
图 13
前面介绍了如何做API设计,那么工程师该如何去调用API呢?答案是状态驱动,如图14所示。首先要在当前的界面初始化一个变量,意味有一个这样的变量表示当前是不是静音,然后在创建Streaming的时候,Streaming的Muted状态是等于该变量的,这样它们两个就绑定在一起了。初始化时,Streaming处于静音的状态,然后比如说有一个按纽的回调方法去触发变量的改变,React Native发现当前的状态发生改变后,就会把所有的属性做一个diff,这是React Native一个比较核心的性能优化点,diff后发现Streaming的Muted属性发生变化,就会通知底层的Native代码,这样就完成了静音的操作。开始和停止推流也是类似的,只要把state设成某一个状态然后去改变它就可以了。
图 14
我们用了这么多代码,它们之间的布局可能会非常不同,那么最终是该如何把这些东西融合在一起,去适配它们呢?React Native本身是采用了W3C的新标准,即弹性盒子,它的方式是设计一些相对的布局,例如你的元素是都靠左,还是都靠右,还是在中间,还是分散它,还是等距离等。当然这只是一方面,它的整个规范是比较复杂的,这里面只简单讲了一个属性,具体可以参考链接(https://demos.scotch.io/visual-guide-to-css3-flexbox-flexbox-playground/demos/)。图15是Flex Box布局标准的一部分。
图 15
图 16
图16是Android Streaming View的一个界面树。我们可以看到它的页面有很多层次,中间还有一个对焦框。其实我们只关心最外面的一层,最终应用开发者使用最外面一层的View即可,对里面的视图并不关心。在Android中我们一般会用XML Layout去布局,但是在设计一个API的时候,给用户很多种方式显然是不太合适的。当你给了用户一个XML,然后说用我们的API的时候,在Android里面还要再去改XML,如果对方是一个Web开发者,它通过React Native技术进入了移动开发领域,看到这个XML之后会感觉很慌,因为并不知道这个是什么。同理,iOS里面也是一样的,里面可能会要用到Auto Layout。这些都是不太推荐的。所以在Android里面,我们是直接用代码进行布局,它的核心界面结构并不是太复杂。iOS里面也是直接保持了最外层的View和里面的View大小一致。但最终开发者使用的,都是用Flex Box来布局。
在开发直播应用的时候,还有一个比较重要的内容,就是如何优雅地获取和释放硬件资源。我们知道,在iOS里面这并不是问题,因为iOS会根据你的应用状况去释放和获取资源,但是在Android里却可能是个问题。在Android里面,你如果在后台录像或录声音会比较危险,这个时候React Native比较贴心地提供了类似的方法,你可以去监听主Activity的做法,然后对应地在你的视图里面做一些响应,去释放一些硬件。
React Native应用中的一些坑
如今,React Native发展得非常迅速,版本升级也是非常频繁。去年大概10月份的时候还是0.14版本,现在已经是0.26版本了,迭代速度非常快,一两周可能就会发一个版本。发新版本时,有的时候可以平滑地升级,但是有的时候会非常地痛苦,那么究竟是升级还是不升级呢?不升级,绑定在某一个版本持续使用,这种选择可能更适用于那种平台本身已经非常成熟的情况。而现在React Native的发展速度极快了,导航栏已经发布了三个版本,新的组件在不断地发布,性能也在持续地优化。例如原来是在JS的线程里面去执行动画,而JS是单线程的,执行动画时会对界面产生一定的影响,所以他们在尝试在其他线程中执行动画。最近还支持了3Dtouch等。因此,最好是紧跟React Native版本更新,至少不要延迟超过两个版本。
然而升级的过程中,也伴随着诸多不适。
第一个是React Native在0.14版本以后,整个图片的加载方式都变了。原因是原来的图片加载方式非常草率,基本上是你估计在哪里然后给你渲染出来就好了。但是大家知道在Android和iOS设备上,会有不同的分辨率大小。后来的版本采用了静态编译的方式来解决这个问题,但是从0.14版本往上升的时候,需要你重新做一大堆的事情去手动升级。
第二个是0.19版本时,Java里面的一个Annotation的包移动了位置,这个小小的升级却产生了非常大的影响,导致所有的第三方的组件都要改动后重新编译,因为所有第三方的扩展组建都非常依赖Annotation。这个过程当中,可能一时兴起写了一个组件丢在那儿不维护了,就会导致了大量的第三方库不可用,而这个问题是非常严重的。第三是在你每次升级Node.js版本之后,并没有提示要把所有的依赖性文件都删除,这样可能会带来一系列问题,而React Native的issues列表里面可能至少有5%都是因为这个问题导致的。
原文链接:https://www.f2er.com/react/306054.html