老早之前就想总结下Webview相关的知识点了,因为互联网大潮中,很多APP都会使用到Webview,像那些不计其数的电商APP,无一例外的使用Webview;或者一些非电商APP中的像广告页面,注册协议页面都会用到;最后因为一些事情拖到现在才做,感觉事情真不能拖,越往后推越做不了,罪过罪过。
怎么总结Webview呢
1.简单介绍
2.WebView/WebViewClient/WebChromeClient api介绍
3.简单使用
4.JS调用Android本地
5.Android调用JS方法
6.缓存处理及性能优化
7.webview使用注意点
webview系列文章
Android之WebView/WebViewClient/WebChromeClient简介 API详述 【一】
Android之WebView/WebViewClient/WebChromeClient 使用样例 【二】
Android之WebView Android调用JS方法 JS调用Android方法 【三】
Android之WebView 使用注意点 JS注入漏洞问题 内存优化【五】
3.简单使用
在xml中添加
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
然后在代码中
private void initWebview() { /** * webview相关设置的管理类,就像你家的大管家一样 */ mSetting = mWebview.getSettings(); /** * 处于安全性考虑,android默认不支持js * 如果页面有js操作,一定要设置 */ mSetting.setJavaScriptEnabled(true); //设置允许js弹框 mSetting.setJavaScriptCanOpenWindowsAutomatically(true); //在webview中启用或禁用内容URL访问,默认启用,可以通过Content provider去访问资源 mSetting.setAllowContentAccess(true); //在webview中禁用或启用文件访问,默认启用,这里针对的是手机内存的文件,assets和res目录下的不受影响 mSetting.setAllowFileAccess(true); //设置缓存模式 mSetting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //设置缓存路径 //Android 私有缓存存储,如果你不调用setAppCachePath方法,WebView将不会产生这个目录。 mSetting.setAppCachePath(MyApplication.getInstance().getCacheDir().getAbsolutePath()); //设置是否启用缓存,不过需要先设置好缓存路径,默认false mSetting.setAppCacheEnabled(true); //设置是否阻止加载网络图片 默认false // mSetting.setBlockNetworkImage(false); //设置是否自动加载图片 默认true // mSetting.setLoadsImagesAutomatically(true); //设置是否阻止加载所有网络资源 默认false // mSetting.setBlockNetworkLoads(false); //设置webview是否应使用其内置的缩放机制 mSetting.setBuiltInZoomControls(true); //设置webview在使用内置缩放控件的时候是否显示屏幕缩放控件 mSetting.setDisplayZoomControls(false); //设置是否启用DOM存储 mSetting.setDomStorageEnabled(true); //设置webview是否能以概貌模式加载页面,即当页面内容宽度大于webview控件的宽度时 mSetting.setLoadWithOverviewMode(true); // 设置 UserAgent 属性 mSetting.setUserAgentString(""); mWebViewClient = new MyWebViewClient(); mWebview.setWebViewClient(mWebViewClient); // mChromeClient = new MyWebChromeClient(); // mWebview.setWebChromeClient(mChromeClient); mWebview.setOnKeyListener(this); //加载网络页面 mWebview.loadUrl("https://www.baidu.com"); //加载assets文件夹下的html // mWebview.loadUrl("file:///android_asset/html/index.html"); //加载手机本地的html页面 // mWebview.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html"); } @Override public boolean onKey(View v, int keyCode, KeyEvent event) { //点击返回键的时候如果有上一页就返回上一页,避免直接退出 if(keyCode == KeyEvent.KEYCODE_BACK && mWebview.canGoBack()){ mWebview.goBack(); return true; } return false; }
public class MyWebChromeClient extends WebChromeClient{ private String TAG = "MyWebChromeClient"; @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); Log.e(TAG,"onProgressChanged newProgress="+newProgress); } @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); Log.e(TAG,"onReceivedTitle title="+title); } @Override public void onReceivedIcon(WebView view, Bitmap icon) { super.onReceivedIcon(view, icon); Log.e(TAG,"onReceivedTitle icon="+icon.getByteCount()); } /** * htmlzhong <video/> 控件在未播放时,会展示成一张图片,html中可以通过poster属性来指定 * 如果没有指定poster属性,我们可以通过这个方法提供一个默认的海报图 * @return */ @Override public Bitmap getDefaultVideoPoster() { Log.e(TAG,"getDefaultVideoPoster"); return super.getDefaultVideoPoster(); } /** * 当全屏的视频正在缓冲时,通过此方法返回一个view * @return */ @Override public View getVideoLoadingProgressView() { Log.e(TAG,"getVideoLoadingProgressView"); return super.getVideoLoadingProgressView(); } @Override public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { Log.e(TAG,"onCreateWindow"); return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg); } @Override public void onCloseWindow(WebView window) { super.onCloseWindow(window); Log.e(TAG,"onCloseWindow"); } /** * 接收js控制台消息 * @param consoleMessage * @return */ @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { Log.e(TAG,"onConsoleMessage"); return super.onConsoleMessage(consoleMessage); } /** * 网页内容请求定位 * @param origin * @param callback */ @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { super.onGeolocationPermissionsShowPrompt(origin, callback); Log.e(TAG,"onGeolocationPermissionsShowPrompt"); } /** * 请求定位取消了 */ @Override public void onGeolocationPermissionsHidePrompt() { super.onGeolocationPermissionsHidePrompt(); Log.e(TAG,"onGeolocationPermissionsHidePrompt"); } /** * 当前页面进入了全屏模式,此时应用必须显示一个包含网页内容的自定义view * @param view * @param callback */ @Override public void onShowCustomView(View view, CustomViewCallback callback) { super.onShowCustomView(view, callback); Log.e(TAG,"onShowCustomView"); } /** * 当前页面已退出全屏模式,但应用程序必须隐藏自定义view */ @Override public void onHideCustomView() { super.onHideCustomView(); Log.e(TAG,"onHideCustomView"); } /** * JS通知android显示一个警告对话框 * @param view * @param url * @param message * @param result * @return 返回true才有效 下面方法同理 */ @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { Log.e(TAG,"onJsAlert"); if (!TextUtils.isEmpty(message)) { //模拟显示一个对话框 Toast.makeText(MyApplication.getInstance(),message,Toast.LENGTH_SHORT).show(); AlertDialog.Builder build = new AlertDialog.Builder(MyApplication.getInstance()); build.setTitle(message); build.setMessage(message); build.setCancelable(true); build.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirm(); } }); build.create().show(); } result.cancel();//调用这个方法,否则可能无效 return true; } /** * JS通知android显示一个确认对话框 * @param view * @param url * @param message * @param result * @return */ @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { Log.e(TAG,"onJsConfirm"); return super.onJsConfirm(view, url, message, result); } /** * JS通知android显示一个对话框让用户选择是否离开当前页面 * @param view * @param url * @param message * @param result * @return */ @Override public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) { Log.e(TAG,"onJsBeforeUnload"); return super.onJsBeforeUnload(view, url, message, result); } /** * JS通知android显示一个提示信息 * @param view * @param url * @param message * @param defaultValue * @param result * @return */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { Log.e(TAG,"onJsPrompt"); return super.onJsPrompt(view, url, message, defaultValue, result); } /** *当页面内容请求本地资源需要请求权限时回调,前提是该权限未被授权或取消 * @param request */ @Override public void onPermissionRequest(PermissionRequest request) { super.onPermissionRequest(request); Log.e(TAG,"onPermissionRequest"); } /** * 申请权限被取消 * @param request */ @Override public void onPermissionRequestCanceled(PermissionRequest request) { super.onPermissionRequestCanceled(request); Log.e(TAG,"onPermissionRequestCanceled"); } /** * 为 html <input type="file"> 显示文件选择器,返回false使用默认处理 * @param webView * @param filePathCallback * @param fileChooserParams * @return */ @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { Log.e(TAG,"onShowFileChooser"); return super.onShowFileChooser(webView, filePathCallback, fileChooserParams); } }
/** * @Description TODO(处理webview的通知和请求) * @author cxy * @Date 2018/6/11 15:10 */ public class MyWebViewClient extends WebViewClient { private String TAG = "MyWebViewClient"; /** * 设置不用系统浏览器打开,直接显示在当前Webview * 在点击请求的是连接是才会调用 * 重写此方法返回true表明点击网页里面的链接还是在当前的webView里跳转,不跳到浏览器里边 * @param view * @param request * @return */ /*@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { Log.i(TAG,"shouldOverrideUrlLoading Title="+view.getTitle()+""+request.getUrl().toString()); return true; }*/ /** * 这个方法在api24即7.0以后废弃了,上面那个方法在24后添加 * @param view * @param url * @return */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Log.i(TAG,"shouldOverrideUrlLoading Title="+view.getTitle()+",url="+url); return true; } /** * 页面开始加载时会回调 一次Frame加载对应一次回调 * 可以在这里显示加载动画,避免长时间的空白画面 * @param view * @param url * @param favicon */ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); Log.i(TAG,"onPageStarted url="+url); } /** * 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。 * @param view * @param url */ @Override public void onLoadResource(WebView view, String url) { super.onLoadResource(view, url); // Log.i(TAG,"onLoadResource url="+url); } /** * 页面加载结束会回调 * @param view * @param url */ @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); Log.i(TAG,"onPageFinished url="+url); } /** * 从服务器收到HTTP错误,错误的状态码大于等于400,任何资源的错误都会回调这个方法 * 比如遇到了404,我们可以设置显示一个本地的错误提示页面,要不然显示404页面对用户不友好 * @param view * @param request * @param errorResponse */ @Override public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { super.onReceivedHttpError(view, request, errorResponse); Log.i(TAG,"onReceivedHttpError errorResponse="+errorResponse.getStatusCode()); } /** * 加载网页时发生ssl错误 * webview默认不处理https请求 * 重写此方法可以让webView处理https请求 * @param view * @param handler * @param error */ @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); Log.i(TAG,"onReceivedSslError error="+error.getPrimaryError()); //表示等待正式响应 该决定会用于以后来响应SSL错误 handler.proceed(); //表示挂起连接,为默认方式 // handler.cancel(); } /** * 处理按键事件 * 系统按键或者shouldoverridekeyevent返回true 不回调此方法 * @param view * @param event */ @Override public void onUnhandledKeyEvent(WebView view, KeyEvent event) { super.onUnhandledKeyEvent(view, event); Log.i(TAG,"onUnhandledKeyEvent event="+event.getKeyCode()); } /** * 重写此方法才能够处理在浏览器中的按键事件 * 给应用一个机会处理按键事件,如果返回true,webview不处理该事件, * 否则webview会一直处理,默认返回false * @param view * @param event * @return */ @Override public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { Log.i(TAG,"shouldOverrideKeyEvent event="+event.getKeyCode()); return super.shouldOverrideKeyEvent(view,event); } /** * WebView 可以拦截某一次的 request 来返回我们自己加载的数据,这个方法在后面缓存会有很大作用。 * * @param view WebView * @param request 当前产生 request 请求 * @return WebResourceResponse */ @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { return super.shouldInterceptRequest(view, request); } }
静待更新
后续篇章请前往Android之WebView/WebViewClient/WebChromeClient使用解析及Android与JS相互调用 【三】