豆瓣混合开发实践

混合开发的直白的解释是 Native 和 Web 技术都要用。但形式上,应用仍然和浏览器无关,用户还是需要在 App Store 和 Android Market 下载 App。只是在开发时,开发者以 Native 代码为主体框架,在合适的地方部分使用 Web 技术。比如在 UIViewController 中放置一个 UIWebview(一个浏览器引擎,只拥有渲染 HTML,CSS 和执行 JavaScript 的核心功能)。这样,部分用户界面就可以使用 Web 技术实现。

促使开发者在移动开发中使用 Web 技术主要动力在于,相比于 Native 技术,Web 技术具有诸多优势:

  • HTML,CSS,JavaScript 的组合被证明在用户界面开发方面具有很高的效率。
  • 统一的浏览器内核标准,使得 Web 技术具有跨平台特性。iOS 和 Android 可以使用一套代码。
  • 可越过发布渠道自主更新应用。

这些优势都和关开发效率的。Web 技术具有这些优势的原因是,Web 技术是一个开发的标准。开放的标准发展出来的庞大生态,而且这个生态从 PC 时代发展至今已积累多年,开发者可以利用生态中产出的各种成果,而省去很多重复工作。在大型移动应用的开发中,项目代码庞杂,通常还是 iOS,Android, 移动 Web 和 桌面 Web 全平台支持。这种情况下,更高的开发效率就成为了开发者不得不考虑的问题。这也是为何虽然移动平台的 Web 技术在使用范围和性能上有诸多劣势,仍然有很多开发者付出努力探索如何在移动开发中使用 Web 技术。

那为何不在移动开发中全面转向 Web 技术呢?并非没有做过这类尝试,但是都失败了。Google 和 Facebook 由于 Web 技术雄厚,早期都对 Web App 做过尝试,但现在都一定程度上回归到 Native 技术。起码大家都意识到,现阶段我们还无法只使用 Web 技术解决移动开发中的大部分问题。

  • 一方面,移动应用的分发渠道已经形成,用户已经习惯在 App Store 和 Android Market 下载应用,而不是在手机浏览器中输入域名。
  • 另一方面,浏览器需要重新定义一套移动设备的交互方式,这项工作由 W3C 推动,但显然这个组织无法跟上 Apple 和 Google 推动各自原生系统发展的速度。这应该是由于移动操作系统还在快速发展之中,还未到可以大量快速标准化的时候。

无论如何,结果就是现在使用纯粹的 Web 技术开发的应用,无论在用户的接受程度上,还是使用体验上都不如 Native 技术开发的应用。

所以,各路开发者们开始思考折衷的方式:就是仍然在 Native 的主体框架下,在合适的地方部分使用 Web 技术。这其中较简单,而直接的,同时也是现阶段广泛使用的就是混合开发(hybrid)。

随着豆瓣 App的发展,团队规模逐渐扩大,项目代码量越来越大,豆瓣App 也成为一个需要提供 iOS,Android 和移动 Web 页面的多平台服务;另一方面我们仍需维持两周一个版本的开发节奏。所以,我们会寻求一些提高团队的开发效率的方法。

项目已经发展到一定程度,我们并没有希望推到以往的开发方式,一切从头再来的野心和勇气。只是希望在不影响 App 的性能前提下,在合适的地方使用 Web 技术部分提高开发效率。而豆瓣 App 中又确实存在部分页面是重度展示,却轻度的交互的页面。这些页面恰恰比较适合使用 Web 技术来实现。

经过团队的一些努力,App 中部分页面已经使用 Web 技术实现,并在取得了不错的效果。工程师使用 Web 技术开发的页面可以部署到两个平台,开发效率得到了实质性提高。就算不提热更新,减少 Android 项目方法数这种附带的好处,我们都已喜欢上这项技术,决定推动在豆瓣移动开发中的推动混合开发的使用。

团队中喜欢玩魔兽的同学将我们的混合开项目命名为 Rexxar(《魔兽世界》中人物,出生于卡利姆多大陆的菲拉斯,同时具有雷骨兽人和南部菲拉斯野生食人魔血统)。项目由移动工程师,前端工程师和后端工程师配合完成。我并未贡献太多代码,所以这里仅仅作为项目的使用者聊一些经验和体会。

Rexxar 主要由以下三部分组成:

  • Rexxar-Route,我们使用 URI 来标识每一个页面。在 App 中通过指明 URI 跳转到此页面。所以,需要一个路由表,可以根据 URI 找到一个 Rexxar-Web 的对应资源来正确展示相应页面;

  • Rexxar-Web,前端代码库,由 HTML、CSS、JavaScript、Image 等组成,用来提供在移动客户端使用的用户页面;

  • Rexxar-Container,一个前端代码的运行容器。它其实是一个内嵌的浏览器(WebView),我们为内嵌浏览器提供了一些必要的原生端支持,包括 API 的 OAuth 授权、图片缓存等;现在有 Android 和 iOS 两个版本的实现。

在项目实践中,Rexxar-Web 和 Rexxar-Route 由一个项目实现,并部署于同一个 Web 项目中。

Rexxar-Route 比较简单,只需要表达一个路由表即可。我们使用了一个 json 文件来表达路由表。给出一个路由表的例子:

  
  
  1. {
  2. count: 4,
  3. items: [{
  4. remote_file: "https://img1.doubanio.com/dae/rexxar/files/orders/orders-70dbdbcb1c.html",
  5. uri: "douban://douban.com/orders[/]?.*"
  6. }, {
  7. remote_file: "https://img1.doubanio.com/dae/rexxar/files/related_doulists/related_doulists-1d7d99e1fb.html",
  8. uri: "douban://douban.com/(tag|tv|movie|book|music)/(\w+)/related_doulists[/]?.*"
  9. }, {
  10. remote_file: "https://img1.doubanio.com/dae/rexxar/files/selection/columns-1a4666ac89.html",
  11. uri: "douban://douban.com/selection/columns[/]?.*"
  12. }, {
  13. remote_file: "https://img3.doubanio.com/dae/rexxar/files/seti/category_channel-2974d9257d.html",
  14. uri: "douban://douban.com/seti/category_channel/(.*)[/]?.*"
  15. }],
  16. sig: "api",
  17. deploy_time: "Fri, 04 Mar 2016 11:12:29 GMT
  18. }

我们发布的每个版本的 App 安装包都会包含最新版本的 routes.json 文件。在 App 启动时,都会尝试下载最新版本的 routes.json。在遇到无法解析的 URI 时,也会去下载新版 routes.json。

Rexxar-Web 是 Rexxar 的前端代码库。我们使用了 React 作为前端开发框架。在 Rexxar-Web 中,我们提供了一些公共前端组件:

  • 数据统计;
  • 相对通用的页面初始数据的支持,以避免加载时的空页面;
  • 通用的错误处理、Loading等效果;
  • 页面点击反馈效果;
  • List 支持;
  • List 上面的操作,定制了 Android(长按) 与 iOS(左划) 的不同交互;
  • Android ActionBar 的简单可定制;

有了这些组件,我们日常产品开发的难度就降低了。普通移动开发工程师经过一段时间的学习,也可以像前端工程师一样,以 Rexxar 为工具为 App 做一些产品开发了。这部分可以视为一个纯粹的前端项目。

我们使用混合开发技术提高开发效率的一个前提是,尽量不损害 App 的使用体验。基于这个前提,Native 和 Web 如何分工方面我们做了一些尝试。首先,为了保证使用体验,我们把 App 里页面切换留给了 Native。这样,每个页面(Controller 或者 Activity)都是一个 Container。Container 一般都是内嵌浏览器。页面内的功能和逻辑在 Native 和 Web 之间如何分工呢?我们尝试过有几种策略:

  • 纯浏览器方案:也就是 Native 除了扔给内嵌浏览器一个 url 地址之外,就没有不做任何事情了,剩余的事情都由 Web 技术完成。这和用 Safari 或 Chrome 等普通浏览器打开一个网页并没有太多区别。只是我们固定了访问的地址。

  • 前端模板渲染容器方案:这种方案大部分事情由 Native 完成,Web 部分只是负责页面元素的呈现,不参与页面界面之外的其他部分。我们在客户端存储了一个 HTML 作为 UI 模板。Native 代码负责获取数据,向 HTML 文件模板中填入动态数据,得到一个可以在内嵌浏览器渲染的 HTML 文件。这个过程有点类似于 Web 框架里模板渲染库(例如,ninja2)的作用。

  • Rexxar-Container 方案:Rexxar 采用的方案介于上述两种方案之间。Rexxar-Container 同样提供了一个运行前端代码的容器。它也是一个内嵌的浏览器(WebView)。只是,我们并不是扔给内嵌浏览器一个 url 地址就放手不管了,而是对内嵌浏览器包装了很多功能。

Rexxar-Container 方案中,Container 需要实现的功能:

  • Rexxar-Route 路由表的更新,已经在客户端的保存;
  • 为 Rexxar-Web 前端代码发出的 API 请求提供包装。带上必要的 OAuth 参数;
  • 缓存 Rexxar-Web 前端代码所需要的静态文件,包括 HTML、CSS、JavaScript、Image(图片素材)等;
  • 存 Rexxar-Web 中所需要加载的资源文件,例如图片等;
  • 通过协议为 Rexxar-Web 提供一些原生支持的功能。

这种实现方案,是基于保证使用体验的前提下,尽量让 Web 技术多做一些事情的考虑。

Rexxar-Container 和 Rexxar-Web 之间的交互

混合开发实践中,一般都会涉及到 Native 和 Web 如何通信的问题。这是因为我们把一件事情交给两种技术完成,那么它们之间便会存在有一些通信和协调。通常会使用 JSBridge(Android:JsBridge,iOS:WebViewJavascriptBridge) 来实现 Native 和 Web 的相互调用。但在 Rexxar 中,我们并没有选择这个方案。这是因为,我们试图尽量缩小 Rexxar-Container 和 Rexxar-Web 所需要的交互。即使有一些交互,我们都事先定义好协议。现在只支持 Rexxar-Web 请求一些定义好的由 Native 实现的功能。而且由于使用场景还未出现这需求,到现在我们仍然不支持 Native 调用 Web 实现的功能。

这些协议是由 Rexxar-Web 代码访问 URI 形式完成。Rexxar-Container 截获这些 URI 请求,调用 Native 代码完成相应的功能:

  • 请求 douban://rexxar.douban.com/log,可以发一条数据统计记录。
  • 请求 douban://rexxar.douban.com/nav_title,可以定义 Navigation Bar Title。
  • 请求 douban://rexxar.douban.com/nav_menu,可以定义 Navigation Bar Button。
  • 请求 douban://rexxar.douban.com/toast,可以出现一个消息通知 toast。

将 Native 和 Web 的通信以协议的形式规范起来,是因为我们希望 Native 和 Web 之间的通信是可定义的,可控的。有这种期望的原因是,我们以 Rexxar 完成的页面,不仅仅使用在 App 内,还会使用在移动 Web 上。我们的移动站点,特别是分享到外部(如微信,微博)的页面也希望复用 Rexxar 在 App 内的成果。如果,任由开发者自由的定义过多的依赖于 App 原生实现的功能,我们就无法顺利地迁移到移动 Web 上去。标准浏览器并不支持 JSBridge 的大部分功能。可以看看我们已经实现的协议,大部分在移动 Web 是被可以忽略的(比如,nav_title, nav_menu),或者我们也可以较容易地以移动 Web 支持的形式再实现一次(比如,toast)。

Rexxar-Container 的技术实现

Rexxar-Container 主要的工作是截获 Rexxar-Web 的数据请求和原生功能请求。数据请求是以 API 的请求形式发出;原生功能请求是以 URI 请求形式发出。Rexxar-Container 截获请求之后,做相应的反应,要么为数据请求加上 OAuth 认证信息,要么按照协议调用某些原生功能。这样 Rexxar-Web 代码在 App 的 Rexxar-Container 内工作方式,就和在普通浏览器里差别不大。代码都是标准 Web 式的,没有为原生移动开发做太多定制。可以顺利移植到 Web 平台,在各种浏览器中都可以正确运行。

我们为 iOS 和 Android 各开发了一个 Rexxar-Container。iOS 和 Android 平台截获请求的方式并不一样:

  • Android 平台的实现是,通过在本地启动一个 Local Web Server 的方式来代理 Rexxar-Web 代码发出的请求。任何 Rexxar-Web 中的资源,都需要经过预处理步骤,它会根据根据 mime-type 或者 文件后缀将 HTML/CSS/Javascript 资源中的“指定 url ”替换指向 localhost 服务器。这样所有请求都会先经过 Local Web Server 的处理。

  • iOS 平台的实现是,通过 NSURLProtocol 来代理“指定 url ”的请求。

Rexxar 工作流

例如,客户端接到一个页面请求,要打开一个 URI:douban://douban.com/movie/1292052。Rexxar 的工作流如下:

  1. 根据 URI 查询本机缓存的路由表,看是否能够找到对应的资源记录(一般是一个 HTML 文件)。如果找到不到,请求 Rexxar-Route 服务,获得最新的全量路由表,更新本地缓存,找到对应的资源记录;

  2. 根据路由表指示的 HTML 文件的路径,看本地是否找到对应的文件。如果找不到,请求 Rexxar-Web 资源服务器,更新本地缓存;

  3. 在 Rexxar-Container 里展示该 HTML 文件;如有需要,在 Container 中请求 Image,先检查本地缓存。如不存在,请求 Rexxar-Web 资源服务器;

  4. Rexxar-Web 前端代码在 Container 里继续执行,发出 API 请求,有 Rexxar-Container 代理这些请求,为 API 请求添加 OAuth 验证;

  5. Rexxar-Web 前端代码继续执行,根据 API 返回的结果,展示响应的页面,可能会请求 CDN 的图片等;

  6. Rexxar-Web 前端代码继续执行,如果需要修改 NavigationBar 等原生界面,可能通过定义好的协议向 douban://rexxar.douban.com 发送数据。Rexxar-Container 拦截请求,按定义好的协议作出反应,例如,修改 NavigationBar 上的按钮。

性能

混合开发的问题在于,Web 的性能没法和 Native 相比。这种状况可能会长期存在。因为,前端代码运行于内嵌浏览器之上,和直接调用原生系统相比,理论上总会存在性能上的差距。我们现在基本是以规避的方式面对性能问题:即性能问题会明显影响到用户体验时,我们就不使用 Rexxar 来做,而是使用传统 Native 的方式老老实实写两份 Native 代码,一份 iOS,一份 Android。当然,这就限缩了 Rexxar 的使用范围。

错误报告

在我们上了 Rexxar 之后,在收集到的 Crash Report 中,JavaScript 的相关错误,和浏览器相关的错误开始增加。而对这类错误,由于移动应用的使用环境更为复杂,错误报告经过了 JavaScript 引擎,原生系统两层之后,给出的错误信息并不够明确。我们在这方面的经验也并不多,导致我们还没有很好的办法降低这类错误。这对提高 App 的稳定性带来了问题。

Rexxar 这个混合开发框架在豆瓣移动开发中使用,确实在一定程度上提高了我们的开发效率。以前一个页面需要 iOS 和 Android 两位工程师各开发一遍,现在只需要一位工程师写一次前端代码,甚至还可以应用到移动 Web 上去。虽然 Rexxar 仍然存在一些问题,和使用上的限制。但是在有限的使用中,我们仍然收获不少。所以,在未来我们应该会持续推动 Rexxar 在豆瓣移动开发中的使用。

查看原文

CSDN海神之光上传的代码均可运行,亲测可用,直接替换数据即可,适合小白; 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b或2023b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描博客文章底部QQ名片; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪(CEEMDAN)、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 1. EMD(经验模态分解,Empirical Mode Decomposition) 2. TVF-EMD(时变滤波的经验模态分解,Time-Varying Filtered Empirical Mode Decomposition) 3. EEMD(集成经验模态分解,Ensemble Empirical Mode Decomposition) 4. VMD(变分模态分解,Variational Mode Decomposition) 5. CEEMDAN(完全自适应噪声集合经验模态分解,Complementary Ensemble Empirical Mode Decomposition with Adaptive Noise) 6. LMD(局部均值分解,Local Mean Decomposition) 7. RLMD(鲁棒局部均值分解, Robust Local Mean Decomposition) 8. ITD(固有时间尺度分解,Intrinsic Time Decomposition) 9. SVMD(逐次变分模态分解,Sequential Variational Mode Decomposition) 10. ICEEMDAN(改进的完全自适应噪声集合经验模态分解,Improved Complementary Ensemble Empirical Mode Decomposition with Adaptive Noise) 11. FMD(特征模式分解,Feature Mode Decomposition) 12. REMD(鲁棒经验模态分解,Robust Empirical Mode Decomposition) 13. SGMD(辛几何模态分解,Spectral-Grouping-based Mode Decomposition) 14. RLMD(鲁棒局部均值分解,Robust Intrinsic Time Decomposition) 15. ESMD(极点对称模态分解, extreme-point symmetric mode decomposition) 16. CEEMD(互补集合经验模态分解,Complementary Ensemble Empirical Mode Decomposition) 17. SSA(奇异谱分析,Singular Spectrum Analysis) 18. SWD(群分解,Swarm Decomposition) 19. RPSEMD(再生相移正弦辅助经验模态分解,Regenerated Phase-shifted Sinusoids assisted Empirical Mode Decomposition) 20. EWT(经验小波变换,Empirical Wavelet Transform) 21. DWT(离散小波变换,Discraete wavelet transform) 22. TDD(时域分解,Time Domain Decomposition) 23. MODWT(最大重叠离散小波变换,Maximal Overlap Discrete Wavelet Transform) 24. MEMD(多元经验模态分解,Multivariate Empirical Mode Decomposition) 25. MVMD(多元变分模态分解,Multivariate Variational Mode Decomposition)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值