Android 抛弃原生WebView,使用腾讯X5内核、并加入广告拦截。
最近在使用,总会出现DNS被拦截的情况。预览了各个大神的论坛与博客。
发现可以更改WebView内核。找到了比较火的两个。
分别是:腾讯X5内核 和 crosswalk
crosswalk : 据说很强大,但缺点就是会让你的APK包增大很多。(我还没试过,都是看大神们的博客说的)
大家可以参考这篇文章 如何轻松搞定Crosswalk之嵌入模式
相对crosswalk呢,腾讯X5 比较适合我目前的项目。至少包不会一下子给我 增大那么多
TBS腾讯浏览服务(点击跳转官网)
腾讯X5的好处我就不再说了,官网解释的肯定比我到位,我怎么做的吧。
第一步:那肯定是下载官方的SDK 包啦(腾讯浏览服务-SDK下载) 我这里下载的是上面这个
第二步:根据SDK 提供的jar包和so 包拷贝到自己的项目下。
(注意:我这里和官方提供的so,放的位置可能有点区别,这个就需要看的项目情况了)
注意:x5暂时不提供64位so文件,为了保证64位手机能正常加载x5内核,请参照如下链接修改相关配置
https://x5.tencent.com/tbs/technical.html#/detail/sdk/1/34cf1488-7dc2-41ca-a77f-0014112bcab7
官方的Demo ,so包是放在 src\main\jniLibs 下这个可以看一下官方包。就知道了
在Demo 中的build.gradle,中有说到 so 包的目录位置
第三步:接下来就开始被配置,初始化X5了,在APP的 Application 中 onCreate() 去初始化
-
private void initX5() {
-
QbSdk.setDownloadWithoutWifi(
true);
-
//x5内核初始化接口//搜集本地tbs内核信息并上报服务器,服务器返回结果决定使用哪个内核。
-
QbSdk.initX5Environment(getApplicationContext(),
new QbSdk.PreInitCallback() {
-
@Override
-
public void onViewInitFinished(boolean arg0) {
-
//x5內核初始化完成的回调,为true表示x5内核加载成功,否则表示x5内核加载失败,会自动切换到系统内核。
-
Log.d(
"app",
" onViewInitFinished is " + arg0);
-
}
-
@Override
-
public void onCoreInitFinished() {
-
}
-
});
-
}
在清单文件中去添加
-
<!-- 腾讯X5内核初始化 -->
-
<service android:name="com.tencent.smtt.export.external.DexClassLoaderProviderService"
-
android:label=
"dexopt"
-
android:process=
":dexopt" />
第四步 :继承 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" />
第六步:如果需要做适配兼容的需要配置以下配置
兼容视频播放:
1)享受页面视频的完整播放体验需要做如下声明:
页面的Activity需要声明 android:configChanges="orientation|screenSize|keyboardHidden"
2)视频为了避免闪屏和透明问题,需要如下设置
a)网页中的视频,上屏幕的时候,可能出现闪烁的情况,需要如下设置:Activity在onCreate时需要设置:
getWindow().setFormat(PixelFormat.TRANSLUCENT);//(这个对宿主没什么影响,建议声明)
b)在非硬绘手机和声明需要controller的网页上,视频切换全屏和全屏切换回页面内会出现视频窗口透明问题,需要如下设置
-
声明当前
<item name="android:windowIsTranslucent">false
</item>为不透明。
-
-
特别说明:这个视各app情况所需,不强制需求,如果声明了,对体验更有利
c)以下接口禁止(直接或反射)调用,避免视频画面无法显示:
-
webview.setLayerType()
-
-
webview.setDrawingCacheEnabled(
true);
第七步:输入法设置
避免输入法界面弹出后遮挡输入光标的问题
方法一:在AndroidManifest.xml中设置
android:windowSoftInputMode="stateHidden|adjustResize"
方法二:在代码中动态设置:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
还有一些其他的配置信息 我这里就不在搬官方的介绍了。
混淆配置、APP自定义UA、Tbs视频播放器接入...等等。官方描述还是很清晰的
好啦,到这里就全部介绍完啦。希望对你帮助。