Hybrid App 应用 开发中 9 个必备知识点复习(WebView / 调试 等) 上

简介: Hybrid App 应用 开发中 9 个必备知识点复习(WebView / 调试 等) 上



前言

我们大前端团队内部 📖每周一练 的知识复习计划继续加油,本篇文章是 《Hybrid APP 混合应用专题》 主题的第二期和第三期的合集。

这一期共整理了 10 个问题,和相应的参考答案,文字和图片较多,建议大家可以收藏,根据文章目录来阅读

之前分享的每周内容,我都整理到掘金收藏集 📔《EFT每周一练》 上啦,欢迎点赞收藏咯💕💕。

内容回顾:

  1. 《EFT 每周分享 —— Hybrid App 应用开发中 5 个必备知识点复习》
  2. 《EFT 每周分享 —— HTTP 的15个常见知识点复习》
  3. 《EFT 每周分享 —— 数据结构与算法合集》

文章收录:

本系列所有文章,都将收录在 Github 上,欢迎点击查阅

注:本文整理部分资料来源网络,有些图片/段落找不到原文出处,如有侵权,联系删除。


一、iOS 平台中 UIWebView 与 WKWebView 有什么区别?

参考文章:《UIWebView与WKWebView》

UIWebView 是苹果继承于 UIView 封装的一个加载 web 内容的类,它可以加载任何远端的web数据展示在你的页面上,你可以像浏览器一样前进后退刷新等操作。不过苹果在 iOS8 以后推出了 WKWebView 来加载 Web,并应用于 iOS 和 OSX 中,它取代了 UIWebViewWebView ,在两个平台上支持同一套 API。

它脱离于 UIWebView 的设计,将原本的设计拆分成14个类,和3个代理协议,虽然是这样但是了解之后其实用法比较简单,依照职责单一的原则,每个协议做的事情根据功能分类。

WKWebViewUIWebView 的区别:

  • WKWebView 的内存远远没有 UIWebView 的开销大,而且没有缓存;
  • WKWebView 拥有高达 60FPS 滚动刷新率及内置手势;
  • WKWebView 支持了更多的 HTML5 特性;
  • WKWebView 高效的 app 和 web 信息交换通道;
  • WKWebView 允许 JavaScriptNitro 库加载并使用, UIWebView 中限制了;
  • WKWebView 目前缺少关于页码相关的 API;
  • WKWebView 提供加载网页进度的属性;
  • WKWebView 使用 Safari 相同的 JavaScript 引擎;
  • WKWebView 增加加载进度属性: estimatedProgress
  • WKWebView 不支持页面缓存,需要自己注入 cookie , 而 UIWebView 是自动注入 cookie
  • WKWebView 无法发送 POST 参数问题;
  • WKWebView 可以和js直接互调函数,不像 UIWebView 需要第三方库 WebViewJavascriptBridge 来协助处理和 js 的交互;

注意:

大多数App需要支持 iOS7 以上的版本,而 WKWebView 只在 iOS8 后才能用,所以需要一个兼容性方案,既 iOS7 下用 UIWebViewiOS8 后用 WKWebView 。但是目前 IOS10 以下的系统以及很少了,

小结:

WKWebView 相较于 UIWebView 在整体上有较大的提升,满足 iOS 上面使用同一套控件的功能,同时对整个内存的开销以及滚动刷新率和 JS 交互做了优化的处理。

依据职责单一原则,拆分成了三个协议去实现 WebView 的响应,解耦了 JS 交互和加载进度的响应处理。

WKWebView 没有做缓存处理,所以对网页需要缓存的加载性能要求没那么高的还是可以考虑 UIWebView


二、WKWebView 有哪一些坑?

参考文章:《WKWebView 那些坑》

1. WKWebView 白屏问题

WKWebView 实际上是个多进程组件,这也是它加载速度更快,内存暂用更低的原因。

UIWebView 上当内存占用太大的时候,App Process 会 crash;而在 WKWebView 上当总体的内存占用比较大的时候,WebContent Process 会 crash,从而出现白屏现象。

解决办法:

  1. 借助 WKNavigtionDelegate

WKWebView 总体内存占用过大,页面即将白屏的时候,系统会调用上面的回调函数,我们在该函数里执行[webView reload](这个时候 webView.URL 取值尚不为 nil)解决白屏问题。在一些高内存消耗的页面可能会频繁刷新当前页面,H5侧也要做相应的适配操作。

  1. 检测 webView.title 是否为空

并不是所有 H5 页面白屏的时候都会调用上面的回调函数,比如,最近遇到在一个高内存消耗的 H5 页面上 present 系统相机,拍照完毕后返回原来页面的时候出现白屏现象(拍照过程消耗了大量内存,导致内存紧张,WebContent Process 被系统挂起),但上面的回调函数并没有被调用。在 WKWebView 白屏的时候,另一种现象是 webView.titile 会被置空, 因此,可以在 viewWillAppear 的时候检测 webView.title 是否为空来 reload 页面。

2. WKWebView Cookie 问题

WKWebViewCookie 问题在于 WKWebView 发起的请求不会自动带上存储于 NSHTTPCookieStorage 容器中的 Cookie,而在 UIWebView 会自动带上 Cookie

原因是:

WKWebView 拥有自己的私有存储,不会将 Cookie 存入到标准的 Cookie 容器NSHTTPCookieStorage 中。

实践发现 WKWebView 实例其实也会将 Cookie 存储于 NSHTTPCookieStorage 中,但存储时机有延迟,在 iOS 8 上,当页面跳转的时候,当前页面的 Cookie 会写入 NSHTTPCookieStorage 中,而在 iOS 10 上,JS 执行 document.cookie 或服务器 set-cookie 注入的 Cookie 会很快同步到 NSHTTPCookieStorage 中,FireFox 工程师曾建议通过 reset WKProcessPool 来触发 Cookie 同步到 NSHTTPCookieStorage 中,实践发现不起作用,并可能会引发当前页面 session cookie 丢失等问题。

解决办法1:

WKWebView loadRequest 前,在 request header 中设置 Cookie, 解决首个请求 Cookie 带不上的问题;

解决办法2:

通过 document.cookie 设置 Cookie 解决后续页面(同域)Ajax``、iframe 请求的 Cookie 问题;(注意:document.cookie() 无法跨域设置 cookie)。

3. WKWebView loadRequest 问题

WKWebView 上通过 loadRequest 发起的 post 请求 body 数据会丢失,同样是由于进程间通信性能问题HTTPBody 字段被丢弃。

4. WKWebView NSURLProtocol问题

WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。

解决办法:

由于 WKWebView 在独立进程里执行网络请求。一旦注册 http(s) scheme 后,网络请求将从 Network Process 发送到 App Process,这样 NSURLProtocol 才能拦截网络请求。在 webkit2 的设计里使用 MessageQueue 进行进程之间的通信,Network Process 会将请求 encode 成一个 Message,然后通过 IPC 发送给 App Process。出于性能的原因,encode 的时候 HTTPBodyHTTPBodyStream 这两个字段会被丢弃掉了。

5. WKWebView 页面样式问题

WKWebView 适配过程中,我们发现部分 H5 页面元素位置向下偏移或被拉伸变形,追踪后发现主要是 H5 页面高度值异常导致。

解决办法:

调整 WKWebView 布局方式,避免调整 webView.scrollView.contentInset 。实际上,即便在 UIWebView 上也不建议直接调整 webView.scrollView.contentInset 的值,这确实会带来一些奇怪的问题。如果某些特殊情况下非得调整 contentInset 不可的话,可以通过下面方式让H5页面恢复正常显示。

6. WKWebView 截屏问题

WKWebView 下通过 -[CALayer renderInContext:]实现截屏的方式失效,需要通过以下方式实现截屏功能:

@implementation UIView (ImageSnapshot) 
- (UIImage*)imageSnapshot { 
    UIGraphicsBeginImageContextWithOptions(self.bounds.size,YES,self.contentScaleFactor);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return newImage; 
} 
@end

然而这种方式依然解决不了 webGL 页面的截屏问题,截屏结果不是空白就是纯黑图片。

解决办法:

无奈之下,我们只能约定一个JS接口,让游戏开发商实现该接口,具体是通过 canvasgetImageData()方法取得图片数据后返回 base64 格式的数据,客户端在需要截图的时候,调用这个JS接口获取base64 String并转换成 UIImage

7. WKWebView crash问题

如果 WKWebView 退出的时候,JS刚好执行了 window.alert(), alert 框可能弹不出来,completionHandler 最后没有被执行,导致 crash

另一种情况是在 WKWebView 一打开,JS就执行 window.alert(),这个时候由于 WKWebView 所在的 UIViewController 出现( pushpresent )的动画尚未结束,alert 框可能弹不出来,completionHandler 最后没有被执行,导致 crash

8. 视频自动播放

WKWebView 需要通过 WKWebViewConfiguration.mediaPlaybackRequiresUserAction 设置是否允许自动播放,但一定要在 WKWebView 初始化之前设置,在 WKWebView 初始化之后设置无效。

9. goBack API问题

WKWebView 上调用 -[WKWebView goBack], 回退到上一个页面后不会触发window.onload() 函数、不会执行JS。

10. 页面滚动速率

WKWebView 需要通过 scrollView delegate 调整滚动速率:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; 
}

三、Crosswalk 是什么,它有什么作用?

参考网站: 《Crosswalk Github》 参考文章: 《Crosswalk入门》

Crosswalk 是一款开源的 web 引擎。目前 Crosswalk 正式支持的移动操作系统包括 Android 和 Tizen ,在 Android 4.0 及以上的系统中使用 Crosswalk 的 Web 应用程序在 HTML5 方面可以有一致的体验,同时和系统的整合交互方面(比如启动画面、权限管理、应用切换、社交分享等等)可以做到类似原生应用

现在 Crosswalk 已经成为众多知名 HTML5 平台和应用的推荐引擎,包括 Google Mobile Chrome App 、 Intel XDK 、 Famo.us 和 Construct2 等等,未来的 Cordova 4.0 也计划集成 Crosswalk

四、常见的 WebView 性能优化方案有哪一些?

0. 问题分析

首先需要了解,对于一个普通用户来讲,打开一个 WebView 通常会经历哪几个阶段,一般有这些:

  1. 交互无反馈;
  2. 到达新的页面,页面白屏;
  3. 页面基本框架出现,但是没有数据;页面处于loading状态;
  4. 出现所需的数据;

当 App 首次打开时,默认是并不初始化浏览器内核的;只有当创建 WebView 实例的时候,才会创建 WebView 的基础框架。

所以与浏览器不同,App 中打开 WebView 的第一步并不是建立连接,而是启动浏览器内核。

于是我们找到了“为什么WebView总是很慢”的原因之一:

  • 在浏览器中,我们输入地址时(甚至在之前),浏览器就可以开始加载页面。
  • 而在客户端中,客户端需要先花费时间初始化 WebView 完成后,才开始加载。

而这段时间,由于WebView还不存在,所有后续的过程是完全阻塞的。因此由于这段过程发生在 native 的代码中,单纯靠前端代码是无法优化的;大部分的方案都是前端和客户端协作完成,以下是几个业界采用过的方案:

1. 全局 WebView

在客户端刚启动时,就初始化一个全局的 WebView 待用,并隐藏,当用户访问了 WebView 时,直接使用这个 WebView 加载对应网页,并展示。

这种方法可以比较有效的减少 WebView 在App中的首次打开时间。当用户访问页面时,不需要初始化 WebView 的时间。

当然这也带来了一些问题,包括:

  • 额外的内存消耗。
  • 页面间跳转需要清空上一个页面的痕迹,更容易内存泄露。

2. WebView 动态加载

参考文章:《WebView常用优化方案》

WebView 动态加载。就是不在 xml 中写 WebView ,写一个 layout ,然后把 WebView add 进去。

WebView mWebView = new WebView(getApplicationgContext());
LinearLayout mll = findViewById(R.id.xxx);
mll.addView(mWebView);
protected void onDestroy() {
    super.onDestroy();
    mWebView.removeAllViews();
    mWebView.destroy()
}

这里用的 getApplicationContext() 也是防止内存溢出,这种方法有一个问题。如果你需要在 WebView 中打开链接或者你打开的页面带有 flash,获得你的 WebView 想弹出一个 dialog ,都会导致从 ApplicationContextActivityContext 的强制类型转换错误,从而导致你应用崩溃。

这是因为在加载 flash 的时候,系统会首先把你的 WebView 作为父控件,然后在该控件上绘制 flash ,他想找一个 ActivityContext 来绘制他,但是你传入的是 ApplicationContext 。然后就崩溃了。

3. 独立的web进程,与主进程隔开

参考文章:《WebView常用优化方案》

这个方法被运用于类似 qq ,微信这样的超级 app 中,这也是解决任何 WebView 内存问题屡试不爽的方法 对于封装的 webactivity ,在 manifest.xml 中设置。

<activity 
    android:name=".webview.WebViewActivity" 
    android:launchMode="singleTop" 
    android:process=":remote" 
    android:screenOrientation="unspecified" 
/>

然后在关闭 webactivity 时销毁进程:

@Overrideprotected void onDestroy() {                
super.onDestroy(); 
    System.exit(0);
}

关闭浏览器后便销毁整个进程,这样一般 95% 的情况下不会造成内存泄漏之类的问题,但这就涉及到 android 进程间通讯,比较不方便处理,优劣参半,也是可选的一个方案。

4. WebView 释放

参考文章:《WebView常用优化方案》

public void destroy() {
    if (mWebView != null) {
        // 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
        // destory()
        ViewParent parent = mWebView.getParent();
        if (parent != null) {
            ((ViewGroup) parent).removeView(mWebView);
        }
        mWebView.stopLoading();
        // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
        mWebView.getSettings().setJavaScriptEnabled(false);
        mWebView.clearHistory();
        mWebView.clearView();
        mWebView.removeAllViews();
        try {
            mWebView.destroy();
        } catch (Throwable ex) {
        }
    }
}


目录
相关文章
|
5天前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
6天前
|
JavaScript 前端开发 Android开发
kotlin开发 webview如何在收到JS调用后,native返回数据给到JS
这段内容描述了在Hybrid App开发中,使用Kotlin的Compose构建的Web视图(WebView)如何通过JsBridge实现JavaScript与原生代码的交互
|
8天前
|
安全 网络安全 API
kotlin安卓开发JetPack Compose 如何使用webview 打开网页时给webview注入cookie
在Jetpack Compose中使用WebView需借助AndroidView。要注入Cookie,首先在`build.gradle`添加WebView依赖,如`androidx.webkit:webkit:1.4.0`。接着创建自定义`ComposableWebView`,通过`CookieManager`设置接受第三方Cookie并注入Cookie字符串。最后在Compose界面使用这个自定义组件加载URL。注意Android 9及以上版本可能需要在网络安全配置中允许第三方Cookie。
|
1月前
|
Web App开发 数据采集 移动开发
开发uniapp过程中对app、微信小程序与h5的webview调试
开发uniapp过程中对app、微信小程序与h5的webview调试
38 1
|
1月前
|
前端开发 Android开发 开发者
【Flutter前端技术开发专栏】Flutter中的混合应用(Hybrid Apps)开发
【4月更文挑战第30天】本文探讨了使用Flutter开发混合应用的方法。混合应用结合Web技术和原生容器,提供快速开发和低成本维护。Flutter,一款现代前端框架,以其插件系统和高性能渲染引擎支持混合应用开发。通过创建Flutter项目、添加平台代码、使用WebView、处理平台间通信以及发布应用,开发者可构建跨平台混合应用。虽然混合应用有性能和用户体验的局限,但Flutter的跨平台兼容性和丰富的插件生态降低了开发成本。开发者应根据项目需求权衡选择。
【Flutter前端技术开发专栏】Flutter中的混合应用(Hybrid Apps)开发
|
1月前
|
移动开发 前端开发 JavaScript
说一说你对混合开发(Hybrid Development)的了解。
混合开发(Hybrid App)结合Web和Native技术,实现跨平台应用开发,降低工作量和时间。使用JavaScript等Web技术提升开发效率,通过优化提供接近原生体验。混合应用可即时更新,维护灵活,成本效益高。React Native和Flutter等框架支持混合开发,丰富的社区资源加速开发进程。网易云音乐等成功案例证明其可行性。随着技术进步,混合开发前景广阔。
33 1
|
12月前
|
Web App开发 移动开发 小程序
|
编解码 移动开发 JavaScript
开发Hybrid App的技术选型
开发Hybrid App的技术选型
139 0
|
移动开发 前端开发 Java
移动端开发必备知识-Hybrid App
开发必备知识-Hybrid App
602 0