大家都不知道原生的WebView 存在各种坑。各种适配问题。
最近在使用,总会出现DNS被拦截的情况。预览了各个大神的论坛与博客。
发现可以更改WebView内核。找到了比较火的两个。
分别是:腾讯X5内核 和 crosswalk
crosswalk : 据说很强大,但缺点就是会让你的APK包增大很多。(我还没试过,都是看大神们的博客说的)
大家可以参考这篇文章 如何轻松搞定Crosswalk之嵌入模式
相对crosswalk呢,腾讯X5 比较适合我目前的项目。至少包不会一下子给我 增大那么多
TBS腾讯浏览服务(点击跳转官网)
腾讯X5的好处我就不再说了,官网解释的肯定比我到位,我怎么做的吧。
第一步:那肯定是下载官方的SDK 包啦(腾讯浏览服务-SDK下载) 我这里下载的是上面这个
第二步:根据SDK 提供的jar包和so 包拷贝到自己的项目下。
(注意:我这里和官方提供的so,放的位置可能有点区别,这个就需要看的项目情况了)
官方的Demo ,so包是放在 src\main\jniLibs 下这个可以看一下官方包。就知道了
在Demo 中的build.gradle,中有说到 so 包的目录位置
第三步:接下来就开始被配置,初始化X5了,在APP的 Application 中 onCreate() 去初始化
private void initX5() { //如果没有这个内核,允许在WIFI情况下去下载内核 QbSdk.setDownloadWithoutWifi(true); //搜集本地tbs内核信息并上报服务器,服务器返回结果决定使用哪个内核。 QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() { @Override public void onViewInitFinished(boolean arg0) { //x5內核初始化完成的回调,为true表示x5内核加载成功,否则表示x5内核加载失败,会自动切换到系统内核。 Log.d("app", " onViewInitFinished is " + arg0); } @Override public void onCoreInitFinished() { } }; //x5内核初始化接口 QbSdk.initX5Environment(getApplicationContext(), cb); }
第四步 :继承 com.tencent.smtt.sdk.WebView 自定义 WebView (这里根据自己的情况定义)
public class SimpleWebView extends com.tencent.smtt.sdk.WebView { public SimpleWebView(Context context) { super(context); init(); } public SimpleWebView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SimpleWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @SuppressLint("SetJavaScriptEnabled") private void init() { WebSettings webSetting = this.getSettings(); webSetting.setJavaScriptEnabled(true); webSetting.setJavaScriptCanOpenWindowsAutomatically(true); webSetting.setAllowFileAccess(true); webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); webSetting.setSupportZoom(true); webSetting.setBuiltInZoomControls(true); webSetting.setUseWideViewPort(true); webSetting.setSupportMultipleWindows(true); // webSetting.setLoadWithOverviewMode(true); webSetting.setAppCacheEnabled(true); // webSetting.setDatabaseEnabled(true); webSetting.setDomStorageEnabled(true); webSetting.setGeolocationEnabled(true); webSetting.setAppCacheMaxSize(Long.MAX_VALUE); // webSetting.setPageCacheCapacity(IX5WebSettings.DEFAULT_CACHE_CAPACITY); webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND); // webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH); webSetting.setCacheMode(WebSettings.LOAD_NO_CACHE); this.setWebViewClient(new SimpleWebViewClient()); this.setWebChromeClient(new WebChromeClient(){ //这里可以设置进度条。但我是用另外一种 @Override public void onProgressChanged(WebView webView, int i) { super.onProgressChanged(webView, i); } }); } public static class SimpleWebViewClient extends com.tencent.smtt.sdk.WebViewClient { private SimpleLoadingDialog loadingDialog; @Override public com.tencent.smtt.export.external.interfaces.WebResourceResponse shouldInterceptRequest(com.tencent.smtt.sdk.WebView webView, String url) { //做广告拦截,ADFIlterTool 为广告拦截工具类 if (!ADFilterTool.hasAd(webView.getContext(),url)){ return super.shouldInterceptRequest(webView, url); }else { return new WebResourceResponse(null,null,null); } } /** * 防止加载网页时调起系统浏览器 */ @Override public boolean shouldOverrideUrlLoading(com.tencent.smtt.sdk.WebView webView, String url) { webView.loadUrl(url); return true; } //在开始的时候,开始loadingDialog @Override public void onPageStarted(com.tencent.smtt.sdk.WebView webView, String s, Bitmap bitmap) { super.onPageStarted(webView, s, bitmap); try{ loadingDialog = new SimpleLoadingDialog(webView.getContext(),true); loadingDialog.show(); }catch (Exception e){} } //在页面加载结束的时候,关闭LoadingDialog @Override public void onPageFinished(com.tencent.smtt.sdk.WebView webView, String s) { super.onPageFinished(webView, s); try { if (loadingDialog != null) { loadingDialog.dismiss(); } } catch (Exception e) {} } @Override public void onReceivedError(com.tencent.smtt.sdk.WebView webView, com.tencent.smtt.export.external.interfaces.WebResourceRequest webResourceRequest, com.tencent.smtt.export.external.interfaces.WebResourceError webResourceError) { super.onReceivedError(webView, webResourceRequest, webResourceError); } @Override public void onReceivedSslError(com.tencent.smtt.sdk.WebView webView, com.tencent.smtt.export.external.interfaces.SslErrorHandler sslErrorHandler, com.tencent.smtt.export.external.interfaces.SslError sslError) { sslErrorHandler.proceed(); } } }
接下来就是使用了,和我们原生的webView 没什么区别啦。
/** * 初始化webView */ @SuppressLint("SetJavaScriptEnabled") private void initDetailsH5() { webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new SimpleWebView.SimpleWebViewClient(){ @Override public void onPageFinished(com.tencent.smtt.sdk.WebView webView, String url) { super.onPageFinished(webView, url); toolbarTitle.setText(webView.getTitle());//获取WebView 的标题,设置到toolbar中去 } @Override public boolean shouldOverrideUrlLoading(com.tencent.smtt.sdk.WebView webView, String url) { if (url.contains("Activity/")){ ... }else if (url.contains("Share")){ ... }else { webView.loadUrl(url); } return true; } }); }
/** * 监听系统返回键,判断WebView是否有上一级,有的话就返回WebView的上一级 * 否则返回页面 * * @param keyCode * @param event * @return */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (webView != null && webView.canGoBack()) { webView.goBack(); return true; } else { return super.onKeyDown(keyCode, event); } } return super.onKeyDown(keyCode, event); }
第五步:这里也根据需要添加吧。就是广告拦截了。广告库加多了的话,可能就会影响性能了。
(我觉的这种做法存在很大的问题,如果建议直接使用Https。就可以避免这个问题)
public class ADFilterTool { /** * 屏蔽广告的NoAdWebViewClient类 * * @param context * @param url * @return true 为广告链接,false 为正常连接 */ public static boolean hasAd(Context context, String url) { Resources res = context.getResources(); String[] adUrls = res.getStringArray(R.array.adBlockUrl); for (String adUrl : adUrls) { if (url.contains(adUrl)) { return true; } } return false; } }
在 res \ vlaues 目录下创建 AdUrlString.xml
(注:广告库部分摘取 Android Webview广告过滤的实现)
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="adBlockUrl"> <item>ubmcmm.baidustatic.com</item> <item>gss1.bdstatic.com/</item> <item>cpro2.baidustatic.com</item> <item>cpro.baidustatic.com</item> <item>lianmeng.360.cn</item> <item>nsclick.baidu.com</item> <item>caclick.baidu.com/</item> <item>jieaogd.com</item> <item>publish-pic-cpu.baidu.com/</item> <item>cpro.baidustatic.com/</item> <item>hao61.net/</item> <item>cpu.baidu.com/</item> <item>pos.baidu.com</item> <item>cbjs.baidu.com</item> <item>cpro.baidu.com</item> <item>images.sohu.com/cs/jsfile/js/c.js</item> <item>union.sogou.com/</item> <item>sogou.com/</item> <item>5txs.cn/</item> <item>liuzhi520.com/</item> <item>yhzm.cc/</item> <item>jieaogd.com</item> <item>a.baidu.com</item> <item>c.baidu.com</item> <item>mlnbike.com</item> <item>alipays://platformapi</item> <item>alipay.com/</item> <item>jieaogd.com</item> <item>vipshop.com</item> </string-array> </resources>
接下来就是 CookieUtils ,同步WebView 的Cookie。为什么要同步Cookie ,我这就不介绍啦。
直接贴代码
/** * Created by Peng on 2017/7/31. */ public class CookieUtils { /**s * 安卓登陆与H5同步Cookie * * @param mUrl */ public static void syncSession(Context context,WebView webView, String mUrl){ HttpUrl httpUrl = HttpUrl.parse(mUrl); if (httpUrl != null){ //注入session CookieManager cm = CookieManager.getInstance(); cm.setAcceptCookie(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cm.removeSessionCookies(new ValueCallback<Boolean>() { @Override public void onReceiveValue(Boolean value) { } }); } else { cm.removeSessionCookie(); } cm.removeAllCookie(); JavaNetCookieJar cookieJar = (JavaNetCookieJar) RetrofitUtil.getOkHttpClient().cookieJar(); List<Cookie> cookies = cookieJar.loadForRequest(httpUrl); for (Cookie cookie : cookies) { cm.setCookie(mUrl, cookie.toString()); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cm.setAcceptThirdPartyCookies(webView,true); CookieManager.getInstance().flush(); } else { CookieSyncManager.createInstance(context).sync(); } } } /**s * 安卓登陆与H5同步Cookie X5内核同步 * * @param mUrl */ public static void syncSession(Context context, com.tencent.smtt.sdk.WebView webView, String mUrl){ HttpUrl httpUrl = HttpUrl.parse(mUrl); if (httpUrl != null){ //注入session com.tencent.smtt.sdk.CookieManager cm = com.tencent.smtt.sdk.CookieManager.getInstance(); cm.setAcceptCookie(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cm.removeSessionCookies(new com.tencent.smtt.sdk.ValueCallback<Boolean>() { @Override public void onReceiveValue(Boolean value) { } }); } else { cm.removeSessionCookie(); } cm.removeAllCookie(); JavaNetCookieJar cookieJar = (JavaNetCookieJar) RetrofitUtil.getOkHttpClient().cookieJar(); List<Cookie> cookies = cookieJar.loadForRequest(httpUrl); for (Cookie cookie : cookies) { cm.setCookie(mUrl, cookie.toString()); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cm.setAcceptThirdPartyCookies(webView,true); CookieManager.getInstance().flush(); } else { CookieSyncManager.createInstance(context).sync(); } } } /** * 同步token * * @param context * @param url * @param token */ public static void syncCookie(Context context, String url,String token){ clearCookie(context); try{ CookieSyncManager.createInstance(context); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.removeSessionCookie();// 移除 cookieManager.removeAllCookie(); String cookieValue = "token=" + token; cookieManager.setCookie(url, cookieValue); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CookieManager.getInstance().flush(); } else { CookieSyncManager.createInstance(context).sync(); } }catch(Exception e){ } } /** * 同步X5 token * * @param context * @param url * @param token */ public static void syncX5Cookie(Context context, String url,String token){ clearX5Cookie(context); try{ com.tencent.smtt.sdk.CookieSyncManager.createInstance(context); com.tencent.smtt.sdk.CookieManager cookieManager = com.tencent.smtt.sdk.CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.removeSessionCookie();// 移除 cookieManager.removeAllCookie(); String cookieValue = "token=" + token; cookieManager.setCookie(url, cookieValue); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { com.tencent.smtt.sdk.CookieManager.getInstance().flush(); } else { com.tencent.smtt.sdk.CookieSyncManager.createInstance(context).sync(); } }catch(Exception e){ } } /** * 清除WebView 缓存以及Cookie * * @param context * @param webView */ public static void clearWebViewCache(Context context, WebView webView){ clearCookie(context); webView.setWebChromeClient(null); webView.setWebViewClient(null); webView.getSettings().setJavaScriptEnabled(false); webView.clearCache(true); } /** * 清除X5WebView 缓存以及Cookie * * @param context * @param webView */ public static void clearWebViewCache(Context context, com.tencent.smtt.sdk.WebView webView){ clearX5Cookie(context); webView.setWebChromeClient(null); webView.setWebViewClient(null); webView.getSettings().setJavaScriptEnabled(false); webView.clearCache(true); } /** * 清除Cookie * * @param context */ public static void clearCookie(Context context){ CookieSyncManager.createInstance(context); //Create a singleton CookieSyncManager within a context CookieManager cookieManager = CookieManager.getInstance(); // the singleton CookieManager instance cookieManager.removeAllCookie();// Removes all cookies. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {// forces sync manager to sync now CookieManager.getInstance().flush(); } else { CookieSyncManager.createInstance(context); CookieSyncManager.getInstance().sync(); } } /** * 清除X5Cookie * * @param context */ public static void clearX5Cookie(Context context){ com.tencent.smtt.sdk.CookieSyncManager.createInstance(context); //Create a singleton CookieSyncManager within a context com.tencent.smtt.sdk.CookieManager cookieManager = com.tencent.smtt.sdk.CookieManager.getInstance(); // the singleton CookieManager instance cookieManager.removeAllCookie();// Removes all cookies. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {// forces sync manager to sync now com.tencent.smtt.sdk.CookieManager.getInstance().flush(); } else { com.tencent.smtt.sdk.CookieSyncManager.createInstance(context); com.tencent.smtt.sdk.CookieSyncManager.getInstance().sync(); } } /** * 停止原生WebView加载,销毁WebView * * @param webView */ public static void destroyWebView(WebView webView){ if (webView != null){ ViewParent parent = webView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(webView); } webView.stopLoading(); webView.clearHistory(); webView.clearView(); webView.removeAllViews(); webView.destroy(); webView = null; } } /** * 停止X5WebView加载,销毁WebView * * @param webView */ public static void destroyWebView(com.tencent.smtt.sdk.WebView webView){ if (webView != null){ ViewParent parent = webView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(webView); } webView.stopLoading(); webView.clearHistory(); webView.clearView(); webView.removeAllViews(); webView.destroy(); webView = null; } } }
最后温馨提醒一下,如果没有添加权限,注意加一下权限噢~
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 硬件加速对X5视频播放非常重要,建议开启 --> <uses-permission android:name="android.permission.GET_TASKS" />
好啦,到这里就全部介绍完啦。希望对你帮助。