当前位置: 主页 > 养生食谱 > 正文

今日头条们的 App 内容页技术实现与优化 CSDN技术—养生网中医养生养生保健食疗养生养生之道最好的养生网站提供生活小常识100个养生小窍门

时间:2021-10-13 来源:南瓜养生网 阅读:185次

  「CSDN 技术」是 CSDN 全力打造的技术专栏,面向一线开发者征稿,专注于深入解读行业的热门技术和场景应用,涉及人工、大据、云计算、移动开发、区块链、据库、端、物联网等众多领域。用瞻的技术视角,为开发者全面剖析技术原理和最新实践,让的开发者紧跟技术,保持警醒的技术嗅觉,对行业技术有更为全面的认知。

  如果你有优质的技术文章,在前沿技术、业务开发上深度的应用实践、场景方案等的新见解,欢迎联系专栏负责人郭芮投稿,联系方式:微信(guorui_1118),邮箱()。

  据关数据显示,截至 2017 年底,中国手机新闻客户端用户规模达到 6.36 亿人,移动 App 已经成为新闻和内容传播的最重要途径之一。而伴行业的竞争和发展,App 中的内容页在提升 App 品质、提升用时长及提升用户黏性等方面,扮演着更为角色,同时也着更大的挑战。

  内容页在呈现上越丰富。新闻资讯作为内容页的主体,逐渐增加了更多的文字样式、内容形式、富媒体、以及广告、投票等更为丰富的元素。

  内容页需要更多扩展区域来提高时长及用户黏性。在资讯主体之外,各个 App 逐渐打造了例如关注模块、推荐阅读模块、评论模块、运营模块等越来越多的扩展阅读区域。

  短视频、直播的争夺越来越激烈。越来越多的新闻 App 都将视频作为独立的模块和独立的内容页进行展示。

  同质化产品竞争激烈。更快的迭代速度、更优质的用户体验、更小的实现成本。

  所以,新闻类 App内容页架构的设计和技术的优化,也要配合产品形态的发展,在越来越复杂的需求挑战下,拥有快速响应的能力和稳定优质的体验。

  本文分析目前主流(DAU)新闻类 App 如今日头条、腾讯新闻、快报、一点资讯等内容页技术方案的,一起探索新闻类 App 内容页的技术实现和优化。

  结合目前主流的内容页实现方式,把内容页分为上下两个部分,为了方便后续的阅读简单定义下关键的名词。

  上部分通常用 WebView 实现。常规括标题 + 作者(关注)+ 资讯内容,我们称为WebView 内容区。

  下半部分是平行于 WebView 的各种扩展内容,常规包括点赞打赏、广告推广、相关推荐,热门评论等等,我们称为Native 扩展区。

  WebView 中每个复杂 UI 呈现、扩展区中每个独立模块,我们都称为模块或组件。

  完整来看,内容页右侧(右滑)普遍为评论页。无论是之前流行的 ScrollView 右滑近期流行的 Push 新页面,这两种方式实现起来都比较简单且较为独立,故本文暂时忽略右侧(右滑)评论的部分。

  不同于微博,新闻类 App 的内容以段落性的文字为主,配合段落间的图片、富媒体等。同时为了满足跨平台的一致呈现、PC 网页的文章转载、不同平台文章的抓取,以及注重阅读而非交互等原因,使用WebView加载渲染本地的 HTML 字符串数据已经成为了新闻类 App 通方案。

  UIWebView 较多的 WebCore、JavaCore Crash,以及系统性的内存泄露 OOM,对整个 App 的稳定性都是极大的隐患。反观 WKWebView,基于独立进程,不会占用 App 的内存计算,同时也不会主 App Crash。所以在系统级的稳定性上,WKWebView 有着极大的优势。

  WKWebView 通过 JIT 大幅优化了 JS 的执行速度,是对于新闻类 App 内容页的使用场景来说,简单的进入、退出页面,且的加载渲染 HTML 字符串,WKWebView 比 UIWebView 慢了(Benchmark)。

  WKWebView 具有更加丰富的接口、更多 HTML 和 CSS 的支持、以及更加友好的 JS 交互。同时 Api 的更新和社区的活跃,长远使用的角度看有着极大的优势。

  通过以上的分析,WkWebView 从系统级的稳定性、性能以及后续扩展性都有很大的优势。通过WKWebViewExtension扩展修复原生 WKWebView,结合HybridPageKit中 WKWebView 的回收复用逻辑,极大程度上解决了原生 WKWebView 的问题,起到了很好的效果。

  通过逐阶段分析耗时,在内容页的使用场景下,WKWebView 从 alloc 到开始渲染这段时间,有着极大的优化空间。在浏览内容页这种场景下,HybridPageKit中通过 WKWebView 的复用回收以及资源缓存,极大降低了 WKWebView 加载渲染 HTML 的时间,使之低于原生 UIWebView。

  对于新闻类 App 内容页的使用场景,一些 WKW癫痫病是什么引起的 ?ebView 的问题并没有必要形成通用的解决方案以兼容代码。比如 POST 请求不能带参数、Java 异步执行等问题,都以通过代码的重构来进行解决。尤其不推荐卡主 Runloop 从而同步 JS 的方式。

  目前,在使用 WKWebView 的过程中,唯一未解决的问题就是、全面的白屏检测方案,从而支持 WKWebView 在任何情况下的 Crash 进行重载。诸如系统 Crash 回调、WebView Title 监听、ContentSize 监听、甚至屏幕随机取色值等方法都不能满足全部的白屏场景。

  对于目前的主流 App 来说,单纯的 WebView 已经无法满足复杂的呈现和逻辑。在页面中合理的处理 WebView 与扩展区中的多种 View 协同滚动,灵活扩展,并且支持下拉刷新、上拉加载等操作,不同的新闻类 App 也有不同的技术方案。

  这种方法相对简单,容易实现内容页各个模块的布局,同时基于 TableView 的刷新逻辑,也能动态的处理各个模块的更新、插入删除,并且支持在更多等。和 WebView 的结合滚动也较为流畅。

  这种方式将 Native 扩展区的模块粒度都区分到 Cell 的层级,列表类型模块只能通过 Cell 或者以 Section 的模式进行管理,同时也无法跨页面的整体复用 UI 及业务逻辑。UI 的布局依赖 TableView 模式,灵活性较差。随着组件类型的增多,非同质性的 View 也没有充分利用 TableView 的复用。

  同时无论使用哪种方式和 WebView 衔接,都影响了 WebView、TableView 的独立渲染展示,增加了维护的困难。并且 Header 与 Inset 对于头部区域的扩展,如下拉刷新等,实现都较为困难。

  这种方式完全独立每个模块的实现,使 UI 和业务逻辑一一对应。对 WebView 的渲染没有干扰,模块的加载和布局灵活管理、复用,模块业务逻辑独立内聚。添加删除模块、实现上拉下拉等操作简单。极大的提高了灵活性和复用的可能。

  由于这种方式需要对 SubView 中的滚动视图进行计算、模块动态更新时整体布局也需手动刷新等,极大的提高的实现的复杂度。

  基于ReusableNestingScrollview,在HybridPageKit中,封装了以上 ScrollView 嵌套逻辑。这样就隐藏了复杂的实现逻辑和边界条件,充分的保留了灵活性的。同时对于内容页的使用场景,精简了嵌套滚动的使用,扩展上拉加载更多及下拉刷新逻辑,使整个方案实现简单、灵活扩展。

  随着核心的 WebView 内容区逐渐支持复杂的呈现方式,单纯的 H5 基础渲染已经满足不了现有的需求,比如视频的交互、音乐的续播、以及各种地图、投票等组件。同时 Web 中复杂的 UI 和逻辑也极大降低了 WebView 的渲染速度,增加了开发和维护的成本。

  为了满足更好的交互体验,资讯内容中富媒体内容逐渐增多,如视频的续播、小窗播放、音乐悬浮播放、内容中插入地图、投票等。同时随着产品功能的迭代,例如图片类型的简单模块,也增加了点击全屏、长按保存、二维码识别、双击扩大等交互。这些复杂的 UI 和逻辑导致 CSS 和 JS 增多,Native 和 Web 的通信增加,以及大量运用 LocalStorage 等浏览器存储,增加了客户端开发和维护的成本。

  对于内容 WebView 中的图片,最简单的作法,就是后台直接下发 Img 标签,依靠 WebView 自身的下载与渲染。但是这种方式灵活度较低、客户端无法合理的控制下载时机、无法做的缓存以及裁剪等。

  对于简单 Img 标签的,即后台数据单独下发图片数据,客户端根据需求自定义选择下载时机及缓存策略。Html 模板中先用占位图占位,Native 下载后替换标签的 src进行展示。这种方式虽然解决了灵活性的问题,但是也带来了整个流程的复杂性,以及多次 IPC 间的通信延迟。

  为了兼顾灵活性,以及缩短图片的 Loading 时间,我们在单独处理图片的同时,替换内容 WebView 中全部图片为 Native,减少不必要的流程及通信,极大提高了加载的速度。

  为了减少实现复杂 UI、复杂交互模块的开发、维护成本、减少模块在 Web 和 Native 间的逻辑流程,提高 Web 中模块的加载展示速度,在HybridPageKit中将 Web 中全部非文字类模块全部 Native 化。

  页面模板使用空 div 占位:结合后台的模板与数据,全部模板中全部非文字类的组件,映射成统一 Class 的 Div,通多唯一的 id 与数据绑定。组件默认实现占位图逻辑,对于同步数据同时设置组件的沈阳哪家医院治癫痫治得好 Size,异步数据则先设置为 0。替换后 WebView 对模板进行渲染。

  渲染完成通过 JS 获取位置:WebView 渲染成功回调,通过 JS 获取全部统一 class 对应 WebView 的 Frame,以及对应的唯一 Id。

  在相应位置粘贴 NativeView:在进行以上两个步骤的同时,进行下载图片数据、NativeView 创建、初始化、异步数据拉取等。在 JS 回调全部位置时,根据位置及 ID,粘贴 Native 组件。

  调整字体大小,组件异步数据拉取:对于异步的变化,在复用逻辑之后,下文将结合一并说明。

  在 Native 化全部非文字类组件之后,面对文章中图片、富媒体数量的增多,以及 Native 扩展区元素的增加,没有复用回收的内容页从滚动性能及内存两个两个方面都面临着挑战。同时,为了更好的提升用户体验,需要对各个组件滚动时的位置进行计算,从而区分不同的区域进行诸如预处理、延迟释放等逻辑。

  继承特殊 ScrollView:目前流行的框架如 alibab 的LazyScrollView,对于实现复用回收机制,都需要继承相应的 ScrollView,这种方式对于 WKWebView 来说,是无法实现的。

  继承特殊 Model:由于滚动复用需要保存 View 对应的数据信息,大部分开源框架需要继承特殊数据 Model,生成对应必要的参数或方法,对于支持多种类型组件的通用框架来说,继承的实现方式不易于扩展和维护。

  View 滚动状态简单:滚动时位置的计算,最简单的方式就是根据屏幕的高度计算是否进入屏幕,对于预加载的需求,绝大部分开源框架也是只是在屏幕区域的上下增加了 Buffer,仍然不能区分具体的状态,如进入 buffer、进入屏幕等,无法满足复杂的业务逻辑。

  由于 View 需要不断的复用回收,所以数据、状态、位置、对应的 View 类型都存储在对应的 Model 中,不但实现了数据驱动易于动态扩展,同时优化了复用的逻辑,也缓存住了 Frame 等关键信息优化了渲染布局逻辑。

  由于滚动复用的模块对应的 View 及数据 Model 种类众多,动态扩展 NSObject、UIView 的情况下,无法通用的逻辑公用。所了更好的支持扩展、更灵活的实现方式,ReusableNestingScrollview中面向通过扩展数据 Protocol,使得任何 Model 轻松实现复用回收对应逻辑。

  在ReusableNestingScrollview中,为了满足更复杂的需求,如视频预加载及自动播放、Gif 预加载及自动播放等,我们扩展了组件在滚动过程中的状态,增加自定义 workRange,使组件在滚动过程中的状态变为 3 种,即 None、prepare 区域及 Visible 区域,更加全面准确的记录状态切换,更加灵活的支持业务场景。同时通过 3 种状态扩展为二级缓存,对 View 在不同级别的缓存设置不同的策略。

  在解决了内容 WebView 中非文字类组件的 Native 化、滚动复用之后,我们将实现思想运用到包含 Native 扩展区的,内容页整体架构中。如果从内容页的维度去看,内容 WebView 也可以算作一个组件,它和扩展区的各种组件一起作为 Container 的子 View,也可以运用上面的ReusableNestingScrollview进行实现和管理。

  所以整个内容页就是从两个维度、运用ReusableNestingScrollview中的实现方法两次实现滚动复用回收、数据驱动、组件自管理以及组件状态切换逻辑。

  面对复杂的需求、以及按需加载、异步拉取等优化体验的策略,在HybridPageKit中也针对相应的场景做了高效的处理。

  当 WebView 中字体大小调整时,需要同时调整全部 Native 组件的位置。我们监听 WebView 的 ContenSize 变化,当变化发生时,重新执行获取组件位置的 JS 语句获得全部组件的新位置。基于滚动复用的逻辑,只需要对在屏幕中的组件 View 的位置进行调整,其余只需要重新对组件对应 Model 的 Frame 进行赋值,极大提升了效率。在基础上,要动态的检测 ContenSize 是否小于屏幕高度,高度小于一屏幕时,要同时调整 Native 扩展区组件的位置。

  对于异步拉取数据的组件,由于初始化时占位 Div 的高度为 0,当数据获取成功,并渲染好组件后,需要首先执行 JS 动态修改对应占位 Div 的大小,之后按照以上的逻辑,重新赋值 Native 组件位置。

  Native 扩展区中的组件不同于 WebView 中的组件,不依赖 We湖北专业治癫痫医院bView 自身渲染。所以当动态调整大小时,之需调整全部 Native 扩展区组件数据 Model 中保存的 Frame 信息,同时调整在屏幕中的组件位置即可。

  在实现了以上技术关键点的基础上,如何合理的设计内容页通用的架构,快速响应内容页的各种需求调整,使整体架构易扩展、易维护,同时有较高的性能及较小的内存占用,成为了整个内容页架构实现的重点。在HybridPageKit中,我们围绕灵活复用、高内聚低耦合、易于实现扩展三个重点的方向,设计实现了基于组件化的内容页整体架构。

  为了满足内容页业务的相对独立,支持快速响应迭代及组件整体复用,内容页整体的结构应满足通用性、易于扩展、以及高内聚低耦合的特点。所以在ReusableNestingScrollview的支持下,采用组件化的方式实现全部内容页业务模块。

  为了达到组件的高内聚、与内容页的低耦合,在HybridPageKit中拆分业务逻辑为独立的组件化的处理单元,每个处理单元通过 MVC 模式实现。其中 Model 作为组件的数据,只需要在实现解析逻辑同时,实现对应 delegate 即可。Controller 只需要实现组件间通信的 delegate,选择性的实现例如 controller 生命周期、webview 关键回调、以及滚动复用相关的方法即可。通过组件的自管理及复用,组件可以集成统一的上报逻辑、业务逻辑到的 Controller 中,并且在不同类型的页面灵活复用。

  为了更好的实现组件化的结构,组件的 Controller 需要在内容页初始化时进行注册。内容页在每个关键的生命周期或业务节点,采用中心化通信,广播执行相应的方法,组件的 Controller 按需实现处理即可。对于新增、删除功能,只需扩展 delegate 中的方法,内容页中触发方法、组件中实现方法即可。

  为了提高 WKWebView 渲染速度,通过建立全局 WKWebView 复用回收池来复用 WKWebView。除了基本的线程安全、复用状态管理等,在进入回收池前要 load 特殊 Url 以维护整个 backFowardList。组件的 View 也是通过全局的复用回收池进行管理,使得相同的组件 View 可以灵活的出内容页、列表页等 App 内各个页面,极大的减少了开发成本,提高运行效率。

  WebView 及组件 View 实现自动回收逻辑,每次在申请新 View 时检测活动队列中 View 的 SuperView 是否为 nil,是则自动回收防止内存泄露,同时增加 View 数量阈值、内存告警自动释放逻辑等。

  对于增加关键的业务节点用于组件业务处理,我们只需扩展 delegate 中的方法,在相关组件中实现。内容页 Controller 中在相应位置,通过统一函数触发广播方法即可。对于增加组件来说,只需创建组件完全独立的 MVC 代码,实现数据解析 Model 并实现滚动复用 delegate,在组件 Controller 中实现 delegate 中需要的方法等待调用,以及初始化时在内容页注册即可。删除组件完全无需操作内容页,删除独立的 MVC 结构并停止注册即可。

  为了实现内容页扩展区的灵活复用,在HybridPageKit中也扩展了非 WebView 类型的内容页。就像文中之前提到的,如果将 WebView 看做一个整体作为一个组件,基于ReusableNestingScrollview的位置动态管理,完全可以替换成普通的 View(类似 Banner 视频内容页),或者可扩展收起的 View(问题回答页面)甚至 tableView 等。所以整个 App 内各种类型的内容页只需要简单的配置,便可进行实现和组件复用。

  通过继承特殊的内容页 Controller 并进行简单的配置,即可生成不同类型的内容页整体架构。框架内集成基本的 Mustache 解析和渲染。结合后台数据,只需实现对应页面中组件 MVC 逻辑即可。其中 Model 只需实现对应 Protocol,Controller 在内容页中注册,实现对应 Protocol 即可。

  新闻类 App 内容页,在 Native 的页面框架下,基于 WebView 进行加载和渲染。所以,从优化的角度就延伸出两个维度,即从 Web 的维度优化,以及从 Native 的维度优化。

  WKWebView 的复用:通过 WKWebView 的复用,极大的缩短了 WebView 从创建到渲染结束的时间。

  利用 HTTP 缓存:对于内容 WebView 中必要的 CSS 以及 JS,以及必要的基础 Icon,可以通过设置 HTTP 缓存,依靠浏览器自身缓存提高效率。同时通过资源 md5 校验以保证刷新资源。

<齐齐哈尔儿童癫痫病医院p>   减少资源请求并发:通过 Native 化全部非文字类的内容,Web 页面只加载最近本的 Html 内容,减少了业务逻辑的资源请求和并发。

  减少 Dom & Java 复杂度:通过 Native 化全部非文字类的内容,极大的减少了 Dom 的复杂度、CSS 的复杂度以及过多的 JS 业务逻辑。

  其它 Web 优化通用方法:精简 Java,使用 iconFont,CSS & Java 文件压缩等。

  基于后台数据以及 Native 化组件,内容页 Html 中模板与数据分离,使得全部资源如图片视频等都可以通过 Native 在合适的时机异步并行加载。不依赖与 Web 的渲染。

  对于内容页关键内容(Webview)的拉取,大部分 App 都放到了列表页中进行。进入内容页时直接从 Cache 中取出内容模板,直接交给 WebView 渲染。基于ReusableNestingScrollview扩展丰富的状态及二级缓存,在页面滚动的过程中各个组件也可以精确的实现按需加载、预加载等逻辑。

  WebView 中非文字类 UI Native 化,极大的缩短了展示所需的流程,减少了进程间通信,减少了 I/O 及图片编解码逻辑,提高了类似图片类的 UI 展示速度。

  组件的解耦与自管理,以及广播 delegate 的实现,为组件的按需加载、按优先级加载提供了基础。对于内容页的各个组件来说,在内容页展示之前大部分是不需要初始化、数据拉取以及渲染的。组件化之后的组件可以根据业务优先级,在不同的关键生命周期回调中实现业务逻辑,以减轻内容页创建、模板拼接以及 WebView 渲染的压力。简单的举例,由于内容 WebView 几乎都大于一屏,扩展区中的全部组件都可以在 WebView 渲染结束后进行 View 创建、网络拉取和渲染等,这样即不影响用户的使用,同时极大的释放了渲染结束前的网络、IPC 及 CPU 压力,提高首屏展示速度。

  基于ReusableNestingScrollview扩展数据 Model,缓存对应 View 的 Frame 信息,结合 View 的滚动复用,极大的减少了 UI 布局的逻辑和计算。页面内组件的滚动复用及页面间的组件复用,也同时减少了组件 View 的初始化耗时。

  基于 App 的技术实现和业务逻辑的优化,如异步执行业务逻辑、 图片编解码优化及资源缓存,DNS 缓存等。

  综上,从一个内容页在列表上的点击,到 WebView 渲染结束,最后到用户的滚动操作,按照时间的顺序,全部的优化策略如下图:

  对于新闻类 App 内容页的完整的解决方案,还有一些基本的技术点,比如模板引擎及模板拼接的模块、JSApi 注入及管理的模块等等,由于篇幅所限,暂且不做深入的展开。

  新闻类 App 的内容页,除去基本的渲染 HTML 数据外,同时也需要支持服务于活动、运营的临时 H5 页面。这些页了和 Native 进行交互,在自定义 JSApi 注入、JSBridge 的选择、后台下发 domain 黑白名单、以及相关的安全性考虑也是整个实现中重要的一环。同时由于 WKWebView 支持复用回收,加载本地 Html 类型的 WebView 应该与加载 H5 的 WebView 在不同的回收复用池分开管理。

  对于内容页图片的管理,绝大多数 App 都将之纳入了 App 统一的图片管理体系中。无论使用哪个开源图片库,在缓存策略上,尽量将内容页图片的缓存策略与其他的有所区分,或者使用LRU + FIFO的缓存策略,避免进入内容页大量图片占用缓存空间,导致列表图片释放。同时从使用的角度来说,重复进入同一篇文章的场景也不会频繁的出现。

  由于各个 App 的数据接口和技术选型不同,在HybridPageKit中只简单的实现了基于 Mustache 的模板拼接,主要是由于它的 logic-less、多终端集成的方便以及开源社区的活跃。对于这部分逻辑,需要根据后台数据的格式及业务需求自定义的扩展。

  内容页整体的实现和优化,依赖整个 App 的技术实现和结构,在实现和优化的过程中,还有许多权衡和妥协,以及许多通用的、细节的优化,这里就不一一赘述。

  文章全部的探索及分析的实现,除对应业务逻辑外,应用封装成三个框架:HybridPageKit、ReusableNestingScrollview以及WKWebViewExtension。最终可以通过几十行代码,完成新闻类 App 多种形式的、高性能、易扩展、组件化的内容页实现。

  有任何疑问,欢迎提交 issue, 或者直接修改提交 PR!返回搜狐,查看更多