WebView
webview
是Android中的一个显示网页的控件,也叫网页视图.是内置webkit内核的高性能浏览器.
它是前端和客户端之间交互的一个媒介,好处就是即提高业务开发的效率,减轻客户端工作量,也能借助到客户端强大的底层能力,缺点自然是前端的性能和功能局限性,需要权衡好两者之间的一个度
除了本身的方法,还提供了几个常用的工具类的部分API
WebChromeClient
方法 | 作用 |
---|---|
onJsAlert(WebView view,String url,String message,JsResult result) | 处理Js中的Alert对话框 |
onJsConfirm(WebView view,String url,String message,JsResult result) | 处理Js中的Confirm对话框 |
onJsPrompt(WebView view,String url,String message,String defaultValue,JsPromptResult result) | 处理Js中的Prompt对话框 |
onReceivedIcon(WebView view, Bitmap icon) | 获得网页的icon |
onReceivedTitle(WebView view, String title) | 获得网页的标题 |
WebViewClient
方法 | 作用 |
---|---|
onPageStared(WebView view,String url) | 通知主程序网页开始加载 |
onPageFinished(WebView view,String url,Bitmap favicon) | 通知主程序,网页加载完毕 |
doUpdateVisitedHistory(WebView view,String url,boolean isReload) | 更新历史记录 |
onLoadResource(WebView view,String url) | 通知主程序WebView即将加载指定url的资源 |
onScaleChanged(WebView view,float oldScale,float newScale) | ViewView的缩放发生改变时调用 |
shouldOverrideKeyEvent(WebView view,KeyEvent event) | 控制webView是否处理按键时间,如果返回true,则WebView不处理,返回false则处理 |
shouldOverrideUrlLoading(WebView view,String url) | 控制对新加载的Url的处理,返回true,说明主程序处理WebView不做处理,返回false意味着WebView会对其进行处理 |
onReceivedError(WebView view,int errorCode,String description,String failingUrl) | 遇到不可恢复的错误信息时调用 |
WebSettings
方法 | 作用 |
---|---|
getSettings() | 返回一个WebSettings对象,用来控制WebView的属性设置 |
loadUrl(String url) | 加载指定的Url |
loadData(String data,String mimeType,String encoding) | 加载指定的Data到WebView中.使用"data:"作为标记头,该方法不能加载网络数据.其中mimeType为数据类型如:textml,image/jpeg. encoding为字符的编码方式 |
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) | 比上面的loadData更加强大 |
setWebViewClient(WebViewClient client) | 为WebView指定一个WebViewClient对象.WebViewClient可以辅助WebView处理各种通知,请求等事件。 |
setWebChromeClient(WebChromeClient client) | 为WebView指定一个WebChromeClient对象,WebChromeClient专门用来辅助WebView处理js的对话框,网站title,网站图标,加载进度条等 |
创建webview
public class MainActivity extends AppCompatActivity {
private WebView webView;
private long exitTime = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = new WebView(this);
webView.setWebViewClient(new WebViewClient() {
//设置在webView点击打开的新网页在当前界面显示,而不跳转到新的浏览器中
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
webView.getSettings().setJavaScriptEnabled(true); //设置WebView属性,运行执行js脚本
webView.loadUrl("xxx"); //调用loadUrl方法为WebView加入链接
setContentView(webView); //调用Activity提供的setContentView将webView显示出来
}
}
其中加载页面资源方式有几种
// 加载网页
webView.loadUrl("https://www.xxx.com")
// 加载apk包内资源
webView.loadUrl("file:///xxx/xxx.html")
// 加载手机本地资源
webView.loadUrl("content://xxx.xxx.html")
// 加载页面部分内容
webView.loadUrl(String data, String mimeType, String encoding)
阻止后退
一般在webview点击后退会直接关闭,需要重写控制它历史记录后退
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
if ((System.currentTimeMillis() - exitTime) > 2000) {
Toast.makeText(getApplicationContext(), "再按一次退出程序",
Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
super.onBackPressed();
}
}
}
获取和设置WebView的Cookie数据
@Override
public void onPageFinished(WebView view, String url) {
// 读取
CookieManager cookieManager = CookieManager.getInstance();
String CookieStr = cookieManager.getCookie(url);
Log.e("HEHE", "Cookies = " + CookieStr);
super.onPageFinished(view, url);
// 设置
CookieSyncManager.createInstance(MainActivity.this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie(url, cookies); //cookies是要设置的cookie字符串
CookieSyncManager.getInstance().sync();
}
Javascript 调用 Native
- 定义一个类,用于将数据暴露出来,JS通过该类暴露的方法来调用Native
-
我们在WebView所在页面使用下述代码:
webview.getSettings().setJavaScriptEnabled(true); webview.addJavascriptInterface(object,"name");
- 然后js或者html中调用对象里的暴露的方法:
setJavaScriptEnabled
是在Android 4.4以前的系统才有效
从Android 4.4开始,Android中的WebView不再是基于WebKit的,而是开始基于Chromium,这个改变 使得WebView的性能大幅提升,并且对HTML5,CSS,JavaScript有了更好的支持!虽然chromium完全取代了以前的WebKit for Android,但Android WebView的API接口并没有变, 与老的版本完全兼容。这样带来的好处是基于WebView构建的APP,无需做任何修改, 就能享受chromium内核的高效与强大。
Android 4.4后WebView的一些注意事项
1.多线程
如果你在子线程中调用WebView的相关方法,而不在UI线程,则可能会出现无法预料的错误。 所以,当你的程序中需要用到多线程时候,也请使用runOnUiThread()方法来保证你关于 WebView的操作是在UI线程中进行的:
runOnUiThread(newRunnable(){
@Override
publicvoid run(){
// Code for WebView goes here
}
});
2.线程阻塞
永远不要阻塞UI线程,这是开发Android程序的一个真理。我们却往往不自觉的一个开发中常犯的错误就是:在UI线程中去等待JavaScript 的回调。 例如:
// This code is BAD and will block the UI thread
webView.loadUrl("javascript:fn()");
while(result ==null) {
Thread.sleep(100);
}
Android 4.4中,提供了新的Api来做这件事情。 evaluateJavascript() 就是专门来异步执行JavaScript代码的
3.evaluateJavascript() 方法
mWebView.evaluateJavascript(script, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//TODO
}
});
4.处理WebView中url的跳转
新版WebView对于自定义scheme的url跳转,新增了更为严格的限制条件。 当你实现了 shouldOverrideUrlLoading() 或 shouldInterceptRequest() 回调,WebView 也只会在跳转url是合法Url时才会跳转
// The URL scheme should be non-hierarchical (no trailing slashes)
privatestaticfinalString APP_SCHEME ="example-app:";
@Override
publicboolean shouldOverrideUrlLoading(WebView view,String url){
if(url.startsWith(APP_SCHEME)){
urlData =URLDecoder.decode(url.substring(APP_SCHEME.length()),"UTF-8");
respondToData(urlData);
returntrue;
}
returnfalse;
}
5.UserAgent变化
mWebView.getSettings().setUserAgentString(ua);
mWebView.getSettings().getUserAgentString();
6.使用addJavascriptInterface()的注意事项
从Android4.2开始。 只有添加 @JavascriptInterface 声明的Java方法才可以被JavaScript调用
class JsObject {
@JavascriptInterface
public String toString() { return "injectedObject"; }
}
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
7.Remote Debugging
新版的WebView还提供了一个很厉害的功能:使用Chrome来调试你运行在WebView中的程序
UIWebView
这是iOS2推出的东西,存在严重的性能和内存消耗问题直到iOS8之后逐渐被WKWebView所取代,所以就不必介绍了
WKWebView
优势
1、在性能、稳定性、功能方面有很大提升
2、更多的支持 HTML5 的特性
3、官方宣称的高达60fps的滚动刷新率以及内置手势
4、Safari 相同的 JavaScript 引擎
5、将 UIWebViewDelegate 与 UIWebView 拆分成了14类与3个协议,包含该更细节功能的实现。
实践
以编程方式创建WKWebView
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:"https://www.apple.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}}
要允许用户对历史记录进行前进后退操作,可以使用goBack()
和goForward()
方法作为按钮操作,如果不想用户进行对应行为可以使用canGoBack
和canGoForward
属性进行禁止
一般来说,网页视图会自动将页面内容中出现的电话号码转为号码链接,点击的时候会自动唤起拨号面榜进行拨号,可以使用不包含phoneNumber
标志的WKDataDetectorTypes
位字段设置dataDetectorTypes
属性
您还可以使用setMagnification(_:centeredAt:)
来以编程方式设置web内容第一次在web视图中显示时的比例。然后,用户可以使用手势改变比例。
还有其他API如下,具体可以查看https://developer.apple.com/d...
配置和偏好
- WKWebViewConfiguration
- WKPreferences
进程分配
- WKProcessPool
页面导航管理
- WKNavigationDelegate
- WKNavigation
- WKNavigationAction
- WKNavigationResponse
- WKBackForwardList
- WKBackForwardListItem
用户界面
- WKUIDelegate
- WKWindowFeatures
脚本注入
- WKUserContentController
- WKScriptMessage
- WKUserScript
缓存数据和持久化
- WKWebsiteDataStore
- WKHTTPCookieStore
- WKHTTPCookieStoreObserver
- WKWebsiteDataRecord
处理WebKit无法处理的URL Scheme类型的资源
- WKURLSchemeHandler
- WKURLSchemeTask
页面结构
- WKFrameInfo
- WKSecurityOrigin
内容拦截
- WKContentRuleList
- WKContentRuleListStore
3D-Touch预览
- WKPreviewElementInfo
- WKPreviewActionItem
- UIPreviewActionItem
Javascript 调用 Native(以下代码未经验证,仅限示例)
1, 通过webView代理方法,拦截webView加载过程中的信息
Android的 webview 提供了 shouldOverrideUrlLoading
方法提供给 Native 拦截
public class CustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
......
// 场景一:拦截请求、接收 scheme
if (url.equals("xxx")) {
// handle
...
// callback
view.loadUrl("javascript:setAllContent(" + json + ");")
return true;
}
return super.shouldOverrideUrlLoading(url);
}
}
当URL即将在当前WebView中加载时,给主机应用程序一个控制控件的机会。如果没有提供WebViewClient,默认的WebView将要求活动管理器选择URL的正确处理程序。如果提供了WebViewClient,返回true会导致当前WebView中止加载URL,而返回false会导致WebView继续像往常一样加载URL。
iOS使用WKNavigationDelegate中的代理方法,拦截自定义的URL来实现JS调用OC方法。
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"haleyaction"]) {
[self handleCustomAction:URL];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
2. 重写Javascript原生方法(alert、confirm、prompt、console.log)
android拦截事件分别为 onJsAlert、onJsConfirm、onConsoleMessage、onJsPrompt
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
builder.setTitle("")
.setMessage(message)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setCancelable(false)
.create()
.show();
return true;
}
iOS因为安全机制,WKWebView
对原生方法做了拦截,需要实现 WKUIDelegate
代理方法,不适用这种方式
注入API
Android 的 Webview
提供了 addJavascriptInterface 方法,支持 Android 4.2 及以上系统。
gpcWebView.addJavascriptInterface(new JavaScriptInterface(), 'nativeApiBridge');
public class JavaScriptInterface {
Context mContext;
JavaScriptInterface(Context c) {
mContext = c;
}
public void share(String webMessage){
// Native 逻辑
}
}
Javascript调用如下
window.NativeApi.share(xxx);
iOS 的 UIWebview
提供了 JavaScriptScore 方法,支持 iOS 7.0 及以上系统。WKWebview
提供了 window.webkit.messageHandlers 方法,支持 iOS 8.0 及以上系统。
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 40.0;
configuration.preferences = preferences;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"share"];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"pickImage"];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"share"];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"pickImage"];
}
Javascript调用如下
window.webkit.messageHandlers.share.postMessage(xxx);
Native 调用 Javascript(以下代码未经验证,仅限示例)
页面只需要把方法暴露到全局即可,主要需要实现Native调用方式
Android 中主要有两种方式实现。在 4.4 以前,通过 loadUrl 方法,执行一段 JS 代码来实现。在 4.4 以后,可以使用 evaluateJavascript 方法实现。loadUrl 方法使用起来方便简洁,但是效率低无法获得返回结果且调用的时候会刷新 WebView。evaluateJavascript 方法效率高获取返回值方便,调用时候不刷新WebView,但是只支持 Android 4.4+。
webView.loadUrl("javascript:" + javaScriptString);
webView.evaluateJavascript(javaScriptString, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value){
xxx
}
});
iOS 在 WKWebview
中可以通过 evaluateJavaScript:javaScriptString 来实现,支持 iOS 8.0 及以上系统。
- (void)webView:(WKWebView *)tmpWebView didFinishNavigation:(WKNavigation *)navigation{
//say()是JS方法名,completionHandler是异步回调block
[webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@",result);
}];
}
// objective-c
[jsContext evaluateJavaScript:@"ZcyJsBridge(ev, data)"]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。