百度APP-Android H5首屏优化实践 - 百度App技术
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><a style="color: black;"><span style="color: black;"><img src="https://mmbiz.qpic.cn/mmbiz_jpg/2HyIlGuO02b6FbY6VVTvaQ5Yae6xEoqPg4qGYsbgib73TuebiciaEsbUPV2S79VY1Sh6oCjyHL7Ba4KleraaYSWqQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></span></a></p><a style="color: black;"><span style="color: black;"><span style="color: black;"><strong style="color: blue;">黑客技术</strong></span></span></a><span style="color: black;">点击右侧关注,<span style="color: black;">认识</span>黑客的世界!</span><a style="color: black;"><span style="color: black;"><img src="https://mmbiz.qpic.cn/mmbiz_jpg/XUfq62QbuNiaFZIbV1icByYIGKRlUcFt6IldAicZsKdD8KXBZkWQz1eHWULt7Sy2XUdKWbFt8oY6f6nL5deCuF9yg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></span></a>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><a style="color: black;"><span style="color: black;"><img src="https://mmbiz.qpic.cn/mmbiz_jpg/mLuicVjtwXKxcibzBxWpAKwnSZeb6ngNr7aAiaHtPibKTYESW0Rkh42soGYnC1mpHwOicVty94OeAH06o2Qjrwe0DsA/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></span></a></p><a style="color: black;"><span style="color: black;"><span style="color: black;"><strong style="color: blue;">Java</strong></span></span><span style="color: black;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;">研发</span>进阶</strong></span></span></a><span style="color: black;">点击右侧关注,<span style="color: black;">把握</span>进阶之路!</span><a style="color: black;"><span style="color: black;"><img src="https://mmbiz.qpic.cn/mmbiz_jpg/XUfq62QbuNiaFZIbV1icByYIGKRlUcFt6IldAicZsKdD8KXBZkWQz1eHWULt7Sy2XUdKWbFt8oY6f6nL5deCuF9yg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></span></a>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><a style="color: black;"><span style="color: black;"><img src="https://mmbiz.qpic.cn/mmbiz_jpg/zWk6tTlgzuAbkTia6GFACevoZSwSfLvEXztAiccT3HS3mkAZ7NX59XITWiaw1CaBIIV8SJJDoicgicCKzyUIYQeC8ibg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></span></a></p><a style="color: black;"><span style="color: black;"><span style="color: black;"><strong style="color: blue;">Python<span style="color: black;">研发</span></strong></span></span></a><span style="color: black;">点击右侧关注,探讨技术<span style="color: black;">专题</span>!</span><a style="color: black;"><span style="color: black;"><img src="https://mmbiz.qpic.cn/mmbiz_jpg/XUfq62QbuNiaFZIbV1icByYIGKRlUcFt6IldAicZsKdD8KXBZkWQz1eHWULt7Sy2XUdKWbFt8oY6f6nL5deCuF9yg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></span></a><span style="color: black;">作者丨<span style="color: black;">kanglei</span></span><span style="color: black;"><span style="color: black;">源自</span>:</span><span style="color: black;">https://segmentfault.com/a/1190000020102343</span>
<h2 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">1、</span>背景</span></strong></h2><span style="color: black;">百度App自2016年上半年尝试Feed流业务形态,至2017年下半年,历经10个版本的迭代,基本完<span style="color: black;">成为了</span><span style="color: black;">制品</span>形态的初步探索。在<span style="color: black;">全部</span>Feed流形态的闭环中,<span style="color: black;">资讯</span>详情页(文中<span style="color: black;">叫作</span>为落地页)<span style="color: black;">做为</span><span style="color: black;">要紧</span>的<span style="color: black;">构成</span>部分,<span style="color: black;">倘若</span>打开页面后,loading时间过长,会严重影响用户体验。<span style="color: black;">因此呢</span><span style="color: black;">咱们</span>针对落地页这种H5的首屏展现速度进行了<span style="color: black;">长时间</span>优化,本文会<span style="color: black;">仔细</span>阐述<span style="color: black;">全部</span>优化思路和技术细节</span>
<h2 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">2、</span><span style="color: black;">办法</span>论</span></strong></h2><span style="color: black;"><span style="color: black;">经过</span>分析用户反馈,<span style="color: black;">发掘</span>当时的落地页从点击到首屏展现平均<span style="color: black;">必须</span>3s的时间,每次用户兴致勃勃的想要浏览感兴趣的<span style="color: black;">文案</span>时,却<span style="color: black;">由于</span>过长的loading时间,而不耐烦的<span style="color: black;">选取</span>了back。为了<span style="color: black;">提高</span>用户体验,<span style="color: black;">咱们</span>进行了以下工作:</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fLo7notors6s3ic1OEe39b9Mn7cAbxXsjrNaX46zH9ATIMHSa2ahQj1A/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;"><span style="color: black;">经过</span>用户反馈、QA测试等多种<span style="color: black;">途径</span>,<span style="color: black;">发掘</span>落地页首屏加载慢问题</span><span style="color: black;">定义首屏性能指标(首屏含图,以<span style="color: black;">照片</span>加载为准;首屏<span style="color: black;">没</span>图,以文字渲染结束为准)</span><span style="color: black;">NA、内核、H5三方针对自己加载H5的流程进行划分并埋点上报</span><span style="color: black;">统计侧<span style="color: black;">按照</span>三端上报的数据产出平均值、80分位值的性能报表</span><span style="color: black;">分析性能报表,找到不<span style="color: black;">恰当</span>的耗时点,并进行优化</span><span style="color: black;">以AB实验方式,对比优化前后的性能报表数据,产出优化效果,<span style="color: black;">同期</span><span style="color: black;">评定</span>用户体验等<span style="color: black;">关联</span>指标</span><span style="color: black;"><span style="color: black;">根据</span>长期优化的方式,<span style="color: black;">持续</span>分析定位性能瓶颈点并优化,以AB实验方式<span style="color: black;">评定</span>效果,<span style="color: black;">最后</span>达到<span style="color: black;">咱们</span>的落地页秒开<span style="color: black;">目的</span></span>
<h2 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">3、</span>Hybrid<span style="color: black;">方法</span>简述及性能瓶颈</span></strong></h2><strong style="color: blue;"><span style="color: black;">(一)<span style="color: black;">方法</span>简述</span></strong><span style="color: black;">优化之前,<span style="color: black;">咱们</span>与业内大<span style="color: black;">都数</span>的App<span style="color: black;">同样</span>,在落地页的技术选型中,为了满足跨平台和动态性的<span style="color: black;">需求</span>,采用了Hybrid这种比较成熟的<span style="color: black;">方法</span>。Hybrid,顾名思义,即混合<span style="color: black;">研发</span>,<span style="color: black;">亦</span><span style="color: black;">便是</span>半原生半Web的方式。页面中的<span style="color: black;">繁杂</span>交互功能采用端能力的方式,调用原生API来实现。成本低,灵活性较好,适合偏信息展示类的H5场景。</span><span style="color: black;">下面用一张图来<span style="color: black;">暗示</span>百度App中Hybrid的实现机制和加载流程</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fgHntjBjPyiaSmicLic5ZSJiczDAE7CiaM3cRZ5NCGu1NXEb3jpD4n2EPMQA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><strong style="color: blue;"><span style="color: black;">(二)性能瓶颈</span></strong><span style="color: black;">为了分析Hybrid<span style="color: black;">方法</span>首屏展现较慢的<span style="color: black;">原由</span>,找到<span style="color: black;">详细</span>的性能瓶颈,客户端和前端分别针对各自加载过程中的<span style="color: black;">重要</span>节点进行埋点统计,并借由性能监控平台日志进行展示,下图是截取的某一天全网用户的落地页首屏展现速度80分位数据</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fzESYbibNiaWwBW2Ml1WwxQsypVJwDuoJIRQmtJibBj7ianAIYHXr3oIXqA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">各<span style="color: black;">周期</span>性能点<span style="color: black;">能够</span>按Hybrid加载流程进行划分,<span style="color: black;">能够</span>看到,从点击到首屏展现,大致<span style="color: black;">必须</span>2600ms,其中初始化NA组件<span style="color: black;">必须</span>350ms,Hybrid初始化<span style="color: black;">必须</span>170ms,前端H5执行JS获取正文并渲染<span style="color: black;">必须</span>1400ms,完成<span style="color: black;">照片</span>加载和渲染<span style="color: black;">必须</span>700ms的时间</span><span style="color: black;"><span style="color: black;">咱们</span><span style="color: black;">详细</span>分析下四个<span style="color: black;">周期</span>的性能损耗<span style="color: black;">重点</span><span style="color: black;">出现</span>在<span style="color: black;">那些</span><span style="color: black;">地区</span>:</span><span style="color: black;">1) 初始化NA组件</span><span style="color: black;">从点击到落地页框架初始化完成,<span style="color: black;">重点</span>工<span style="color: black;">做为</span>初始化WebView,尤其是<span style="color: black;">第1</span>次进入(WebView首次创建耗时均值为500ms)</span><span style="color: black;">2) Hybrid初始化</span><span style="color: black;">这个<span style="color: black;">周期</span>的工作<span style="color: black;">重点</span><span style="color: black;">包括</span>两部分,一个是<span style="color: black;">按照</span>调起协议中传入的<span style="color: black;">关联</span>参数,校验解压下发到本地的Hybrid模板,大致<span style="color: black;">必须</span>100ms的时间;<span style="color: black;">另外</span>,WebView.loadUrl执行后,会触发对Hybrid模板头部和Body的解析</span><span style="color: black;">3) 正文加载&渲染</span><span style="color: black;">执行到这个<span style="color: black;">周期</span>,内核<span style="color: black;">已然</span>完<span style="color: black;">成为了</span>对Hybrid模板头部和body的解析,此时<span style="color: black;">必须</span>加载解析页面所需的JS文件,并<span style="color: black;">经过</span>JS调用端能力发起对正文数据的请求,客户端从Server拿到数据后,用JsCallback的方式回传给前端,前端<span style="color: black;">必须</span>对客户端传来的JSON格式的正文数据进行解析,并构造DOM结构,<span style="color: black;">从而</span>触发内核的渲染流程;</span><span style="color: black;">此过程中,<span style="color: black;">触及</span>到对JS的请求,加载、解析、执行等一系列<span style="color: black;">过程</span>,并且存在端能力调用、JSON解析、构造DOM等操作,较为耗时</span><span style="color: black;">4) <span style="color: black;">照片</span>加载</span><span style="color: black;">第(3)步中,前端获取到的正文数据<span style="color: black;">包括</span>落地页的<span style="color: black;">照片</span><span style="color: black;">位置</span>集,在完成正文的渲染后,<span style="color: black;">必须</span>前端再次执行<span style="color: black;">照片</span>请求的端能力,客户端这边接收到<span style="color: black;">照片</span><span style="color: black;">位置</span>集后按<span style="color: black;">次序</span>请求服务器,完成下载后,客户端会调用一次IO将文件写入缓存,<span style="color: black;">同期</span>将对应<span style="color: black;">照片</span>的本地<span style="color: black;">位置</span>回传给前端,<span style="color: black;">最后</span><span style="color: black;">经过</span>内核再发起一次IO操作获取到<span style="color: black;">照片</span>数据流,进行渲染;</span><span style="color: black;">总体来看,<span style="color: black;">照片</span>渲染的时间依赖前端的解析效率、端能力执行效率、下载速度、IO速度等<span style="color: black;">原因</span></span><span style="color: black;"><span style="color: black;">经过</span>分析,延伸出对Hybrid<span style="color: black;">方法</span>的<span style="color: black;">有些</span>思考:</span><span style="color: black;">渲染<span style="color: black;">为何</span>这么慢</span><span style="color: black;"><span style="color: black;">照片</span>请求能否提前</span><span style="color: black;">串行<span style="color: black;">规律</span><span style="color: black;">是不是</span><span style="color: black;">能够</span>改为并行</span><span style="color: black;">WebView初始化时间<span style="color: black;">是不是</span>还<span style="color: black;">能够</span>优化</span>
<h2 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">4、</span>百度App落地页优化<span style="color: black;">方法</span></span></strong></h2><strong style="color: blue;"><span style="color: black;">(一)CloudHybrid</span></strong><span style="color: black;">基于之前对Hybrid性能的分析,<span style="color: black;">咱们</span>内部孵化了一个叫做CloudHybrid的项目,用来<span style="color: black;">处理</span>落地页首屏展现慢的痛点;一句话来形容CloudHybrid<span style="color: black;">方法</span>,<span style="color: black;">便是</span>采用后端直出+预取+拦截的方式,简化页面渲染流程,提前化&并行化网络请求<span style="color: black;">规律</span>,<span style="color: black;">从而</span><span style="color: black;">提高</span>H5首屏速度</span><span style="color: black;">1.后端直出-快速渲染首屏</span><span style="color: black;">a. 页面静态直出 </span><span style="color: black;"><span style="color: black;">针对</span>Hybrid<span style="color: black;">方法</span><span style="color: black;">来讲</span>,端上预置和加载的html文件只是一个模板文件,内部<span style="color: black;">包括</span><span style="color: black;">有些</span>简单的JS和CSS文件,端上加载HTML后,<span style="color: black;">必须</span>执行JS<span style="color: black;">经过</span>端能力从Server异步请求正文数据,得到数据后,还<span style="color: black;">必须</span>解析JSON,构造DOM,应用CSS样式等一系列耗时的<span style="color: black;">过程</span>,<span style="color: black;">最后</span><span style="color: black;">才可</span>由内核进行渲染上屏;</span><span style="color: black;">为了<span style="color: black;">提高</span>首屏展示速度,<span style="color: black;">能够</span>利用后端渲染技术(smarty)对正文数据和前端代码进行整合,直出首屏内容,直出后的html文件<span style="color: black;">包括</span>首屏展现所需的内容和样式,内核<span style="color: black;">能够</span>直接渲染;首屏外的内容(<span style="color: black;">包含</span><span style="color: black;">关联</span><span style="color: black;">举荐</span>、<span style="color: black;">宣传</span>等)<span style="color: black;">能够</span>在内核渲染完首屏后,执行JS,并利用preact进行异步渲染</span><span style="color: black;">百度APP直出<span style="color: black;">方法</span>:</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fbLajDQpZrto3gGic1WxBPPp9jHKQW2XmDILOhKFFarvzVkuuZaqbVGQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;"><span style="color: black;">针对</span>客户端<span style="color: black;">来讲</span>,从CDN中拉取到的html都是<span style="color: black;">已然</span>在server渲染好首屏的,<span style="color: black;">这般</span>的内容<span style="color: black;">没</span>需二次加工,展现速度<span style="color: black;">能够</span>大大<span style="color: black;">提高</span>,仅直出一点,手百Feed落地页的首屏性能数据就从2600ms优化到2000ms以内</span><span style="color: black;">b. 动态信息回填</span><span style="color: black;">为了<span style="color: black;">保准</span>首屏渲染结果的准确性,除了在server侧对正文内容和前端代码进行整合外,还<span style="color: black;">必须</span><span style="color: black;">有些</span>影响页面渲染的客户端状态信息,例如首图<span style="color: black;">位置</span>、字体<span style="color: black;">体积</span>、夜间模式等</span><span style="color: black;"><span style="color: black;">这儿</span><span style="color: black;">咱们</span>采用动态回填的方式,前端会在直出的html中定义一系列特殊字符,用来占位;客户端在loadUrl之前,会利用正则匹配的方式,<span style="color: black;">查询</span>这些占位字符,并<span style="color: black;">根据</span>协议映射成端信息;经过客户端回填处理后的html内容,<span style="color: black;">已然</span>具备了展现首屏的所有<span style="color: black;">要求</span></span><span style="color: black;">c. 动画间渲染</span><span style="color: black;">先看下优化前后效果(上图:优化前;下图:优化后):</span><img src="https://mmbiz.qpic.cn/mmbiz_gif/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fzm13qDP7CrJ0CsQNJic7F1YUzNzyRZiajSJs7qNicWL8GW74b8ArVPO9A/640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1" style="width: 50%; margin-bottom: 20px;"><img src="https://mmbiz.qpic.cn/mmbiz_gif/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fzbATUm1mscKKeROJoyMQUr5jlX2rMw2NUte78sehqRSC49PXWqGtGg/640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">正常<span style="color: black;">来讲</span>,直出后的页面展现速度<span style="color: black;">已然</span><span style="color: black;">火速</span>了;但在<span style="color: black;">实质</span><span style="color: black;">研发</span>中,你可能会遇到即使自己的数据加载速度再快,仍然会<span style="color: black;">显现</span>Activity切换过程中<span style="color: black;">没</span>法渲染H5页面的问题(可以<span style="color: black;">经过</span><span style="color: black;">研发</span>者模式放慢动画时间来验证),产生视觉上的白屏现象(如上面上图)</span><span style="color: black;"><span style="color: black;">咱们</span><span style="color: black;">经过</span><span style="color: black;">科研</span>源码<span style="color: black;">发掘</span>,系统处理view绘制的时候,有一个属性setDrawDuringWindowsAnimating,从命名<span style="color: black;">能够</span>看出来,这个属性是用来<span style="color: black;">掌控</span>window做动画的过程中<span style="color: black;">是不是</span><span style="color: black;">能够</span>正常绘制,而恰好在Android 4.2到Android N之间,系统为了组件切换的流程性<span style="color: black;">思虑</span>,该字段为false,<span style="color: black;">咱们</span><span style="color: black;">能够</span>利用反射的方式去手动修改这个属性,改进后的效果见上面下图</span><span style="color: black;"><span style="color: black;">private</span> <span style="color: black;">void</span> <span style="color: black;">setDrawDuringWindowsAnimating</span><span style="color: black;">(View view)</span> </span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">{</p> <span style="color: black;">if</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">(Build.VERSION.SDK_INT > Build.VERSION_CODES.M</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {</p> <span style="color: black;">return</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">;</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p> <span style="color: black;">if</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> handleDispatchDoneAnimating(view);</p> <span style="color: black;">return</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">;</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p> <span style="color: black;">try</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> {</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> ViewParent rootParent = view.getRootView().getParent();</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> Method method = rootParent.getClass()</p>.getDeclaredMethod(<span style="color: black;">"setDrawDuringWindowsAnimating"</span>, <span style="color: black;">boolean</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">.class);</p> method.setAccessible(<span style="color: black;">true</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p> method.invoke(rootParent, <span style="color: black;">true</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p> } <span style="color: black;">catch</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> (Exception e) {</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">e.printStackTrace();</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p> <span style="color: black;"><span style="color: black;">private</span> <span style="color: black;">void</span> <span style="color: black;">handleDispatchDoneAnimating</span><span style="color: black;">(View paramView)</span> </span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">{</p> <span style="color: black;">try</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> {</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> ViewParent localViewParent = paramView.getRootView().getParent();</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Class localClass = localViewParent.getClass();</p> Method localMethod = localClass.getDeclaredMethod(<span style="color: black;">"handleDispatchDoneAnimating"</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p> localMethod.setAccessible(<span style="color: black;">true</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">localMethod.invoke(localViewParent);</p> } <span style="color: black;">catch</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> (Exception localException) {</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> localException.printStackTrace();</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p><span style="color: black;">2.智能预取-提前化网络请求</span><span style="color: black;">经过直出的改造之后,为了更快的渲染首屏,减少过程中<span style="color: black;">触及</span>到的网络请求耗时,<span style="color: black;">咱们</span><span style="color: black;">能够</span><span style="color: black;">根据</span><span style="color: black;">必定</span>的策略和<span style="color: black;">机会</span>,提前从CDN中请求部分落地页html,缓存到本地,<span style="color: black;">这般</span>当用户点击查看<span style="color: black;">资讯</span>时,只需从缓存中加载<span style="color: black;">就可</span></span><span style="color: black;">手百预取服务架构图</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fozAtBK758s049PXOE6rZ7z4DwNIzSFBKbrFRxbtQ6Ou5R1h2fH0l4Q/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;"><span style="color: black;">日前</span>手百预取服务支撑着图文、图集、视频、<span style="color: black;">宣传</span>等多个业务方,<span style="color: black;">按照</span>业务场景的<span style="color: black;">区别</span>,触发<span style="color: black;">机会</span><span style="color: black;">能够</span>自定义,<span style="color: black;">亦</span><span style="color: black;">能够</span>遵循<span style="color: black;">咱们</span>默认的刷新、滑停、点击等<span style="color: black;">机会</span>,<span style="color: black;">另外</span>,<span style="color: black;">咱们</span>会对预取内容进行优先级排序(<span style="color: black;">按照</span>资源类型、触发<span style="color: black;">机会</span>),会动态的<span style="color: black;">按照</span>当前手机状态信息进行并发<span style="color: black;">掌控</span>和流量<span style="color: black;">掌控</span>,在<span style="color: black;">有些</span>降级场景中,server还<span style="color: black;">能够</span><span style="color: black;">经过</span>云控的方式来<span style="color: black;">掌控</span><span style="color: black;">是不是</span>预取以及预取的数量</span><span style="color: black;">3.通用拦截-缓存共享、请求并行</span><span style="color: black;">在落地页中,除了文本外,<span style="color: black;">照片</span><span style="color: black;">亦</span>是<span style="color: black;">要紧</span>的<span style="color: black;">构成</span>部分。</span><span style="color: black;">直出<span style="color: black;">处理</span>了文字展现的速度问题,但<span style="color: black;">照片</span>的加载渲染速度仍<span style="color: black;">不睬</span>想,尤其是首屏中带有<span style="color: black;">照片</span>的<span style="color: black;">文案</span>,其首图的渲染速度才是真正的首屏时间点</span><span style="color: black;">传统Hybrid<span style="color: black;">方法</span>,前端页面<span style="color: black;">经过</span>端能力调用NA<span style="color: black;">照片</span>下载能力来缓存和渲染<span style="color: black;">照片</span>,虽然实现了客户端和前端<span style="color: black;">照片</span>缓存的共享,但<span style="color: black;">因为</span>JS执行<span style="color: black;">机会</span>较晚,且多次端能力调用存在效率问题,<span style="color: black;">引起</span><span style="color: black;">照片</span>渲染延后</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fyI8ic4d34TSQVwkZp3aFZG9iakicv7hDppztnPIB0qs3GM1B320ZbYCHg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">初步改进<span style="color: black;">方法</span>:</span><span style="color: black;">为了<span style="color: black;">提高</span><span style="color: black;">照片</span>加载速度,减少JS调用耗时,改为纯H5请求<span style="color: black;">照片</span>,速度虽然有所<span style="color: black;">提高</span>,<span style="color: black;">然则</span>客户端和前端缓存<span style="color: black;">没</span>法共享,当点击<span style="color: black;">照片</span>调起NA<span style="color: black;">照片</span>查看器时,<span style="color: black;">没</span>法做到沉浸式效果,且仍需重复下载一次<span style="color: black;">照片</span>,<span style="color: black;">导致</span>流量浪费</span><span style="color: black;">终极<span style="color: black;">方法</span>:</span><span style="color: black;">借由内核的shouldInterceptRequest回调,拦截落地页<span style="color: black;">照片</span>请求,由客户端调用NA<span style="color: black;">照片</span>下载框架进行下载,并以管道方式填充到内核的WebResourceResponse中</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fE3zZbCTFlHhiaI0iaMXxibxy4tvW4B6OMaiaxP7kzHibypicIzq6Z057ib2Fw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">此<span style="color: black;">方法</span>在满足<span style="color: black;">照片</span>渲染速度的<span style="color: black;">同期</span>,解耦了客户端和前端代码,客户端充当server角色,对<span style="color: black;">照片</span>进行请求和缓存<span style="color: black;">掌控</span>,<span style="color: black;">保准</span>前端和客户端<span style="color: black;">能够</span>共用<span style="color: black;">照片</span>缓存,改造后的<span style="color: black;">方法</span>,非首图展现流程,页面不卡顿,首屏80分位值缩短80ms~150ms</span><span style="color: black;">效果如下(上图:</span><span style="color: black;">优化前Hybrid<span style="color: black;">方法</span>;</span><span style="color: black;">下图:</span><span style="color: black;">优化后通用拦截<span style="color: black;">方法</span>):</span><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">4.整体<span style="color: black;">方法</span>流程</span><img src="https://mmbiz.qpic.cn/mmbiz_jpg/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fSIVlh66qlStWurw6w8VcaibkzbU3Mu2vGic2q2UVrjOM9KaallG9zRDg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><strong style="color: blue;"><span style="color: black;">(二)新的优化尝试</span></strong><span style="color: black;">1.WebView预创建</span><span style="color: black;">为了节省WebView的性能损耗,<span style="color: black;">咱们</span><span style="color: black;">能够</span>在合适<span style="color: black;">机会</span>提前创建好WebView,并存入缓存池,当页面<span style="color: black;">必须</span><span style="color: black;">表示</span>内容时,直接从缓存池获取创建好的WebView,<span style="color: black;">按照</span>性能数据<span style="color: black;">表示</span>,WebView预创建<span style="color: black;">能够</span><span style="color: black;">提高</span>首屏渲染时间200ms+</span><img src="https://mmbiz.qpic.cn/mmbiz_png/zWk6tTlgzuCX6UEZSOKic6y2nks4mEo9fszVejoG08yEHUibu5KOpAuKkJaSEicaJM7W2jMEomicZPRw0p8kbanTYg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;"><span style="color: black;">详细</span>以Feed落地页为例,当用户进入手百并触发Feed吸顶操作后,<span style="color: black;">咱们</span>会创建<span style="color: black;">第1</span>个WebView,当用户进入落地页后,会从缓存池中取出来渲染H5页面,为了不影响页面的加载速度,<span style="color: black;">同期</span><span style="color: black;">保准</span>下次进入落地页缓存池中仍然有可用的WebView组件,<span style="color: black;">咱们</span>会在每次页面加载完成(pageFinish)<span style="color: black;">或</span>back退出落地页的<span style="color: black;">机会</span>,去触发预创建WebView的<span style="color: black;">规律</span></span><span style="color: black;"><span style="color: black;">因为</span>WebView的初始化<span style="color: black;">必须</span>和context进行绑定,若想实现预创建的<span style="color: black;">规律</span>,<span style="color: black;">必须</span><span style="color: black;">保准</span>context的一致性,常规做法<span style="color: black;">咱们</span><span style="color: black;">思虑</span><span style="color: black;">能够</span>用fragment来实现承载H5页面的容器,<span style="color: black;">这般</span>context<span style="color: black;">能够</span>用外层的activity实例,但Fragment本身的切换流畅度存在<span style="color: black;">必定</span>问题,并且<span style="color: black;">这般</span>做限定了WebView预创建适用的场景。</span><span style="color: black;">为此,</span><span style="color: black;"><span style="color: black;">咱们</span>找到了一种更加完美的替代<span style="color: black;">方法</span>,即</span><span style="color: black;">MutableContextWrapper</span><span style="color: black;">Special version of ContextWrapper that allows the base context to be modified after it is initially set. Change the base context for this ContextWrapper. All calls will then be delegated to the base context. Unlike ContextWrapper, the base context can be changed even after one is already set.</span><span style="color: black;">简单<span style="color: black;">来讲</span>,<span style="color: black;">便是</span>一种新的context包装类,<span style="color: black;">准许</span><span style="color: black;">外边</span>修改它的baseContext,并且所有ContextWrapper调用的<span style="color: black;">办法</span>都会代理到baseContext来执行</span><span style="color: black;">下面是截取的一段预创建WebView的代码</span><span style="color: black;">@DebugTrace</span> <span style="color: black;"><span style="color: black;">public</span> <span style="color: black;">void</span> <span style="color: black;">prepareNewWebView</span><span style="color: black;">()</span> </span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">{</p> <span style="color: black;">if</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> (mCachedWebViewStack.size() < CACHED_WEBVIEW_MAX_NUM) {</p>mCachedWebViewStack.push(<span style="color: black;">new</span> WebView(<span style="color: black;">new</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> MutableContextWrapper(getAppContext())));</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p> <span style="color: black;"><span style="color: black;">private</span> WebView <span style="color: black;">acquireWebViewInternal</span><span style="color: black;">(Context context)</span> </span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">{</p> <span style="color: black;">if</span>(mCachedWebViewStack ==<span style="color: black;">null</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> || mCachedWebViewStack.isEmpty()) {</p> <span style="color: black;">return</span> <span style="color: black;">new</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> WebView(context);</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> WebView webView = mCachedWebViewStack.pop();</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">MutableContextWrapper contextWrapper = (MutableContextWrapper) webView.getContext();</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> contextWrapper.setBaseContext(context);</p> <span style="color: black;">return</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> webView;</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"> }</p><span style="color: black;">2.NA组件懒加载</span><span style="color: black;">a. WebView初始化完成,立刻loadUrl,<span style="color: black;">没</span>需等待框架onCreate<span style="color: black;">或</span>OnResume结束</span><span style="color: black;">b. WebView初始完成后到页面首屏绘制完成之间,<span style="color: black;">尽可能</span>减少UI线程的其他操作,繁忙的UI线程会拖慢WebView.loadUrl的速度</span><span style="color: black;"><span style="color: black;">详细</span>到Feed落地页场景,<span style="color: black;">因为</span><span style="color: black;">咱们</span>的落地页<span style="color: black;">包括</span>两部分,WebView+NA评论组件,正常流程会在WebView初始化结束后,<span style="color: black;">起始</span>评论组件的初始化及评论数据的获取。</span><span style="color: black;"><span style="color: black;">因为</span>此时评论的初始化仍处在onCreate的UI<span style="color: black;">信息</span>处理中,会严重延迟内核加载主文档的<span style="color: black;">规律</span>。</span><span style="color: black;"><span style="color: black;">思虑</span>到用户进入落地页的时候,评论组件对用户<span style="color: black;">来讲</span>并不可见,<span style="color: black;">因此</span>将评论组件的初始化延迟到页面的pageFinish<span style="color: black;">机会</span><span style="color: black;">或</span>firstScreenPaintFinished;</span><span style="color: black;">80分位性能<span style="color: black;">提高</span>60ms~100ms</span><span style="color: black;">3.内核优化</span><span style="color: black;">a. 内核渲染优化:</span><span style="color: black;">内核中<span style="color: black;">重点</span>分为三个线程(IOThread、MainThread、ParserThread),<span style="color: black;">首要</span>IOThread会从网络端<span style="color: black;">或</span>本地获取html数据,并把数据交给MainThread(渲染线程,<span style="color: black;">非常</span>繁忙,用于JS执行,页面布局等),为了<span style="color: black;">保准</span>MainThread不被阻塞,<span style="color: black;">必须</span>额外起一个后台线程(ParserThread)用来做html的解析工作。</span><span style="color: black;">ParserThread每解析到落地页html中带有特殊class标记的一个div标签<span style="color: black;">或</span>P标签(图中的first、second)时,就会触发一次MainThread的layout工作,并把layout后得到的高度与屏幕高度进行对比,<span style="color: black;">倘若</span>当前layout高度<span style="color: black;">已然</span>大于屏幕高度,<span style="color: black;">咱们</span>认为首屏内容<span style="color: black;">已然</span>完成布局,<span style="color: black;">能够</span>触发渲染上屏<span style="color: black;">规律</span>,</span><span style="color: black;">不必等到整篇html<span style="color: black;">所有</span>解析完成再上屏,提前了首屏的渲染时间;</span><span style="color: black;">80分位下,内核的渲染优化<span style="color: black;">能够</span><span style="color: black;">提高</span>首屏速度100ms~200ms</span><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">b. 预加载JS:</span><span style="color: black;">预创建好WebView后,<span style="color: black;">经过</span>预加载JS(与内核约定好的JS内容,内核侧执行该JS时,只做初始化操作),触发WebView初始化<span style="color: black;">规律</span>,缩短后续加载url耗时;</span><span style="color: black;">80分位性能<span style="color: black;">提高</span>80ms<span style="color: black;">上下</span></span>
<h2 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">5、</span>新的问题-流量和速度的平衡</span></strong></h2><span style="color: black;">频繁预取会带来流量的浪费:</span><span style="color: black;">预取的命中率虽然达到了90%以上,但有效率仅有15%</span><span style="color: black;"><span style="color: black;">处理</span>思路:</span><span style="color: black;">&nbsp&nbsp1.压缩预取的包<span style="color: black;">体积</span>,减少下行流量</span><span style="color: black;">&nbsp&nbsp2.少预取<span style="color: black;">或</span>不预取</span><span style="color: black;">(一)精简预取数据:</span><span style="color: black;">图文:</span><span style="color: black;">优化直出html中内联的css、icon等数据,数据<span style="color: black;">体积</span>减少约40%</span><span style="color: black;">(二)后端智能预取:</span><span style="color: black;">1) 图文:</span><span style="color: black;"><span style="color: black;">经过</span>对图文资源进行评分,来决定4G<span style="color: black;">是不是</span><span style="color: black;">必须</span>预取,多组AB<span style="color: black;">实验</span>最优效果劣化9.5ms</span><span style="color: black;">2)视频:</span><span style="color: black;">为了平衡性能和流量,在性能劣化可接受的范围内(视频起播时间劣化100ms),针对视频部分采用流量高峰期不预取的策略,减少视频总流量约7%,整体带宽峰值下降3%</span><span style="color: black;">(三)AI智能预取</span><span style="color: black;">通用用户操作<span style="color: black;">行径</span>,对Feed预取进行AI预测,减少<span style="color: black;">没</span>效预取的数量。</span>
<h2 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">6、</span>总结&展望</span></strong></h2><span style="color: black;">(一)优化总结</span><span style="color: black;">在总结之前,先来看下整体优化的前后效果对比(上图:</span><span style="color: black;">优化前;</span><span style="color: black;">下图:</span><span style="color: black;">优化后):</span><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;"><span style="color: black;">能够</span>看到,经过一系列的优化手段,落地页<span style="color: black;">已然</span>实现了秒开效果。</span><span style="color: black;">回顾所做的事情,从分析用户反馈到定位性能瓶颈,再到<span style="color: black;">各样</span>优化尝试,<span style="color: black;">发掘</span>所有类似的性能优化手段都<span style="color: black;">能够</span>从以下几点入手:</span><span style="color: black;">提前做:</span><span style="color: black;"><span style="color: black;">包含</span>预创建WebView和预取数据</span><span style="color: black;">并行做:</span><span style="color: black;"><span style="color: black;">包含</span><span style="color: black;">照片</span>直出&拦截加载,框架初始化<span style="color: black;">周期</span>开启异步线程准备数据等</span><span style="color: black;">轻量化:</span><span style="color: black;"><span style="color: black;">针对</span>前端<span style="color: black;">来讲</span>,要<span style="color: black;">尽可能</span>减少页面<span style="color: black;">体积</span>,删减不必要的JS和CSS,不仅<span style="color: black;">能够</span>缩短网络请求时间,还能<span style="color: black;">提高</span>内核解析时间</span><span style="color: black;">简单化:</span><span style="color: black;"><span style="color: black;">针对</span>简单的信息展示页面,对内容动态性<span style="color: black;">需求</span>不高的场景,<span style="color: black;">能够</span><span style="color: black;">思虑</span><span style="color: black;">运用</span>直出替代hybrid,展示内容直接可渲染,<span style="color: black;">没</span>需JS异步加载</span><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">(二)TODO</span><span style="color: black;">页面的更新机制,<span style="color: black;">日前</span><span style="color: black;">方法</span>仅适用于偏静态页面,<span style="color: black;">针对</span>动态性<span style="color: black;">需求</span>较高的业务,<span style="color: black;">必须</span><span style="color: black;">供给</span>页面更新机制,<span style="color: black;">保准</span>每次<span style="color: black;">表示</span>的正确性</span><span style="color: black;">开源之路:</span><span style="color: black;">后续计划将<span style="color: black;">咱们</span>总结下来的这套<span style="color: black;">方法</span>打包开源,前行之路必定坎坷,<span style="color: black;">期盼</span><span style="color: black;">大众</span>多多支持</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;"> <span style="color: black;">举荐</span>↓↓↓ </span></strong></span></p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">长</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">按</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">关</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">注</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;"> 百度seo优化论坛 http://www.fok120.com/ 在遇到你之前,我对人世间是否有真正的圣人是怀疑的。 谷歌外贸网站优化技术。
页:
[1]