webview.loadUrl("http://www.baidu.com/"); //加载web资源
//webView.loadUrl("file:///android_asset/example.html"); //加载本地资源
//这个时候发现一个问题,启动应用后,自动的打开了系统内置的浏览器,解决这个问题需要为webview设置 WebViewClient,并重写方法:
webview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
//返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
return true;
}
//还可以重写其他的方法
});
//java
//调用无参方法
mWebView.loadUrl("javascript:callByAndroid()");
//调用有参方法
mWebView.loadUrl("javascript:showData(" + result + ")");
//javascript,下面是对应的js代码
if (Build.VERSION.SDK_INT < 18) {
mWebView.loadUrl(jsStr);
} else {
mWebView.evaluateJavascript(jsStr, new ValueCallback() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
mWebView.getSettings().setJavaScriptEnabled(true);
public class JSObject {
private Context mContext;
public JSObject(Context context) {
mContext = context;
}
@JavascriptInterface
public String showToast(String text) {
Toast.show(mContext, text, Toast.LENGTH_SHORT).show();
return "success";
}
/**
* 前端代码嵌入js:
* imageClick 名应和js函数方法名一致
*
* @param src 图片的链接
*/
@JavascriptInterface
public void imageClick(String src) {
Log.e("imageClick", "----点击了图片");
}
/**
* 网页使用的js,方法无参数
*/
@JavascriptInterface
public void startFunction() {
Log.e("startFunction", "----无参");
}
}
//特定版本下会存在漏洞
mWebView.addJavascriptInterface(new JSObject(this), "yc逗比");
function showToast(){
var result = myObj.showToast("我是来自web的Toast");
}
function showToast(){
myObj.imageClick("图片");
}
function showToast(){
myObj.startFunction();
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//假定传入进来的 url = "js://openActivity?arg1=111&arg2=222",代表需要打开本地页面,并且带入相应的参数
Uri uri = Uri.parse(url);
String scheme = uri.getScheme();
//如果 scheme 为 js,代表为预先约定的 js 协议
if (scheme.equals("js")) {
//如果 authority 为 openActivity,代表 web 需要打开一个本地的页面
if (uri.getAuthority().equals("openActivity")) {
//解析 web 页面带过来的相关参数
HashMap params = new HashMap<>();
Set collection = uri.getQueryParameterNames();
for (String name : collection) {
params.put(name, uri.getQueryParameter(name));
}
Intent intent = new Intent(getContext(), MainActivity.class);
intent.putExtra("params", params);
getContext().startActivity(intent);
}
//代表应用内部处理完成
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
function openActivity(){
document.location = "js://openActivity?arg1=111&arg2=222";
}
//java
mWebView.loadUrl("javascript:returnResult(" + result + ")");
//javascript
function returnResult(result){
alert("result is" + result);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
//假定传入进来的 message = "js://openActivity?arg1=111&arg2=222",代表需要打开本地页面,并且带入相应的参数
Uri uri = Uri.parse(message);
String scheme = uri.getScheme();
if (scheme.equals("js")) {
if (uri.getAuthority().equals("openActivity")) {
HashMap params = new HashMap<>();
Set collection = uri.getQueryParameterNames();
for (String name : collection) {
params.put(name, uri.getQueryParameter(name));
}
Intent intent = new Intent(getContext(), MainActivity.class);
intent.putExtra("params", params);
getContext().startActivity(intent);
//代表应用内部处理完成
result.confirm("success");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
function clickprompt(){
var result=prompt("js://openActivity?arg1=111&arg2=222");
alert("open activity " + result);
}
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);
//清除当前webview访问的历史记录//只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();
public class X5WebViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
getIntentData();
initTitle();
initWebView();
webView.loadUrl(mUrl);
// 处理 作为三方浏览器打开传过来的值
getDataFromBrowser(getIntent());
}
/**
* 使用singleTask启动模式的Activity在系统中只会存在一个实例。
* 如果这个实例已经存在,intent就会通过onNewIntent传递到这个Activity。
* 否则新的Activity实例被创建。
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
getDataFromBrowser(intent);
}
/**
* 作为三方浏览器打开传过来的值
* Scheme: https
* host: www.jianshu.com
* path: /p/yc
* url = scheme + "://" + host + path;
*/
private void getDataFromBrowser(Intent intent) {
Uri data = intent.getData();
if (data != null) {
try {
String scheme = data.getScheme();
String host = data.getHost();
String path = data.getPath();
String text = "Scheme: " + scheme + "\n" + "host: " + host + "\n" + "path: " + path;
Log.e("data", text);
String url = scheme + "://" + host + path;
webView.loadUrl(url);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 当缩放改变的时候会调用该方法
* @param view view
* @param oldScale 之前的缩放比例
* @param newScale 现在缩放比例
*/
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
//视频全屏播放按返回页面被放大的问题
if (newScale - oldScale > 7) {
//异常放大,缩回去。
view.setInitialScale((int) (oldScale / newScale * 100));
}
}
//初始化的时候设置,具体代码在X5WebView类中
if(Build.VERSION.SDK_INT >= KITKAT) {
//设置网页在加载的时候暂时不加载图片
ws.setLoadsImagesAutomatically(true);
} else {
ws.setLoadsImagesAutomatically(false);
}
/**
* 当页面加载完成会调用该方法
* @param view view
* @param url url链接
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//页面finish后再发起图片加载
if(!webView.getSettings().getLoadsImagesAutomatically()) {
webView.getSettings().setLoadsImagesAutomatically(true);
}
}
/**
* 请求网络出现error
* @param view view
* @param errorCode 错误
* @param description description
* @param failingUrl 失败链接
*/
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (errorCode == 404) {
//用javascript隐藏系统定义的404页面信息
String data = "Page NO FOUND!";
view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
} else {
if (webListener!=null){
webListener.showErrorView();
}
}
}
// 向主机应用程序报告Web资源加载错误。这些错误通常表明无法连接到服务器。
// 值得注意的是,不同的是过时的版本的回调,新的版本将被称为任何资源(iframe,图像等)
// 不仅为主页。因此,建议在回调过程中执行最低要求的工作。
// 6.0 之后
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
X5WebUtils.log("服务器异常"+error.getDescription().toString());
}
//ToastUtils.showToast("服务器异常6.0之后");
//当加载错误时,就让它加载本地错误网页文件
//mWebView.loadUrl("file:///android_asset/errorpage/error.html");
if (webListener!=null){
webListener.showErrorView();
}
}
/**
* 这个方法主要是监听标题变化操作的
* @param view view
* @param title 标题
*/
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (title.contains("404") || title.contains("网页无法打开")){
if (webListener!=null){
webListener.showErrorView();
}
} else {
// 设置title
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
/**
* 在加载资源时通知主机应用程序发生SSL错误
* 作用:处理https请求
* @param view view
* @param handler handler
* @param error error
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
if (error!=null){
String url = error.getUrl();
X5WebUtils.log("onReceivedSslError----异常url----"+url);
}
//https忽略证书问题
if (handler!=null){
//表示等待证书响应
handler.proceed();
// handler.cancel(); //表示挂起连接,为默认方式
// handler.handleMessage(null); //可做其他处理
}
}
@Override
protected void onDestroy() {
try {
//有音频播放的web页面的销毁逻辑
//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
//但是注意:webview调用destory时,webview仍绑定在Activity上
//这是由于自定义webview构建时传入了该Activity的context对象
//因此需要先从父容器中移除webview,然后再销毁webview:
if (webView != null) {
ViewGroup parent = (ViewGroup) webView.getParent();
if (parent != null) {
parent.removeView(webView);
}
webView.removeAllViews();
webView.destroy();
webView = null;
}
} catch (Exception e) {
Log.e("X5WebViewActivity", e.getMessage());
}
super.onDestroy();
}
DNS
connection
服务器处理
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String host = Uri.parse(url).getHost();
LoggerUtils.i("host:" + host);
if (!BuildConfig.IS_DEBUG) {
if (Arrays.binarySearch(domainList, host) < 0) {
//不在白名单内,非法网址,这个时候给用户强烈而明显的提示
} else {
//合法网址
}
}
}
//在onStop里面设置setJavaScriptEnabled(false);
//在onResume里面设置setJavaScriptEnabled(true)。
@Override
protected void onResume() {
super.onResume();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(true);
}
}
@Override
protected void onStop() {
super.onStop();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(false);
}
}
//正确
pb.setVisibility(View.VISIBLE);
mWebView.loadUrl("https://github.com/yangchong211/LifeHelper");
//不太好
@Override
public void onPageStarted(WebView webView, String s, Bitmap bitmap) {
super.onPageStarted(webView, s, bitmap);
//设定加载开始的操作
pb.setVisibility(View.VISIBLE);
}
//下面这个是监听进度条进度变化的逻辑
mWebView.getX5WebChromeClient().setWebListener(interWebListener);
mWebView.getX5WebViewClient().setWebListener(interWebListener);
private InterWebListener interWebListener = new InterWebListener() {
@Override
public void hindProgressBar() {
pb.setVisibility(View.GONE);
}
@Override
public void showErrorView() {
}
@Override
public void startProgress(int newProgress) {
pb.setProgress(newProgress);
}
@Override
public void showTitle(String title) {
}
};
/设置是否开启密码保存功能,不建议开启,默认已经做了处理,存在盗取密码的危险
mX5WebView.setSavePassword(false);
webView.loadData(htmlString, "text/html", "utf-8");
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d("yc", view.getContentheight() + "");
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webView.postDelayed(new Runnable() {
@Override
public void run() {
int contentHeight = webView.getContentHeight();
int viewHeight = webView.getHeight();
}
}, 500);
}
});
/**
* 只有在主页面加载出现错误时,才会回调这个方法。这正是展示加载错误页面最合适的方法。
* 然而,如果不管三七二十一直接展示错误页面的话,那很有可能会误判,给用户造成经常加载页面失败的错觉。
* 由于不同的WebView实现可能不一样,所以我们首先需要排除几种误判的例子:
* 1.加载失败的url跟WebView里的url不是同一个url,排除;
* 2.errorCode=-1,表明是ERROR_UNKNOWN的错误,为了保证不误判,排除
* 3failingUrl=null&errorCode=-12,由于错误的url是空而不是ERROR_BAD_URL,排除
* @param webView webView
* @param errorCode errorCode
* @param description description
* @param failingUrl failingUrl
*/
@Override
public void onReceivedError(WebView webView, int errorCode,
String description, String failingUrl) {
super.onReceivedError(webView, errorCode, description, failingUrl);
// -12 == EventHandle.ERROR_BAD_URL, a hide return code inside android.net.http package
if ((failingUrl != null && !failingUrl.equals(webView.getUrl())
&& !failingUrl.equals(webView.getOriginalUrl())) /* not subresource error*/
|| (failingUrl == null && errorCode != -12) /*not bad url*/
|| errorCode == -1) { //当 errorCode = -1 且错误信息为 net::ERR_CACHE_MISS
return;
}
if (!TextUtils.isEmpty(failingUrl)) {
if (failingUrl.equals(webView.getUrl())) {
//做自己的错误操作,比如自定义错误页面
}
}
}
/**
* 只有在主页面加载出现错误时,才会回调这个方法。这正是展示加载错误页面最合适的方法。
* 然而,如果不管三七二十一直接展示错误页面的话,那很有可能会误判,给用户造成经常加载页面失败的错觉。
* 由于不同的WebView实现可能不一样,所以我们首先需要排除几种误判的例子:
* 1.加载失败的url跟WebView里的url不是同一个url,排除;
* 2.errorCode=-1,表明是ERROR_UNKNOWN的错误,为了保证不误判,排除
* 3failingUrl=null&errorCode=-12,由于错误的url是空而不是ERROR_BAD_URL,排除
* @param webView webView
* @param webResourceRequest webResourceRequest
* @param webResourceError webResourceError
*/
@Override
public void onReceivedError(WebView webView, WebResourceRequest webResourceRequest,
WebResourceError webResourceError) {
super.onReceivedError(webView, webResourceRequest, webResourceError);
}
/**
* 任何HTTP请求产生的错误都会回调这个方法,包括主页面的html文档请求,iframe、图片等资源请求。
* 在这个回调中,由于混杂了很多请求,不适合用来展示加载错误的页面,而适合做监控报警。
* 当某个URL,或者某个资源收到大量报警时,说明页面或资源可能存在问题,这时候可以让相关运营及时响应修改。
* @param webView webView
* @param webResourceRequest webResourceRequest
* @param webResourceResponse webResourceResponse
*/
@Override
public void onReceivedHttpError(WebView webView, WebResourceRequest webResourceRequest,
WebResourceResponse webResourceResponse) {
super.onReceivedHttpError(webView, webResourceRequest, webResourceResponse);
}
/**
* 任何HTTPS请求,遇到SSL错误时都会回调这个方法。
* 比较正确的做法是让用户选择是否信任这个网站,这时候可以弹出信任选择框供用户选择(大部分正规浏览器是这么做的)。
* 有时候,针对自己的网站,可以让一些特定的网站,不管其证书是否存在问题,都让用户信任它。
* 坑:有时候部分手机打开页面报错,绝招:让自己网站的所有二级域都是可信任的。
* @param webView webView
* @param sslErrorHandler sslErrorHandler
* @param sslError sslError
*/
@Override
public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {
super.onReceivedSslError(webView, sslErrorHandler, sslError);
//判断网站是否是可信任的,与自己网站host作比较
if (WebViewUtils.isYCHost(webView.getUrl())) {
//如果是自己的网站,则继续使用SSL证书
sslErrorHandler.proceed();
} else {
super.onReceivedSslError(webView, sslErrorHandler, sslError);
}
}
// 网页内容的宽度是否可大于WebView控件的宽度
ws.setLoadWithOverviewMode(false);
webView.addJavascriptInterface(javaObj, "jsObj");
/**
* 同步cookie
*
* @param url 地址
* @param cookieList 需要添加的Cookie值,以键值对的方式:key=value
*/
private void syncCookie (Context context , String url, ArrayList cookieList) {
//初始化
CookieSyncManager.createInstance(context);
//获取对象
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
//移除
cookieManager.removeSessionCookie();
//添加
if (cookieList != null && cookieList.size() > 0) {
for (String cookie : cookieList) {
cookieManager.setCookie(url, cookie);
}
}
String cookies = cookieManager.getCookie(url);
X5LogUtils.d("cookies-------"+cookies);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.flush();
} else {
CookieSyncManager.getInstance().sync();
}
}
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setJavaScriptEnabled(true);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
mWebView.getSettings().setBlockNetworkImage(false);
@SuppressLint("NewApi")
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//handler.proceed();// 接受证书
super.onReceivedSslError(view, handler, error);
}
//将js对象与java对象进行映射
webView.addJavascriptInterface(new ImageJavascriptInterface(context), "imagelistener");
@Override
public void onPageFinished(WebView view, String url) {
X5LogUtils.i("-------onPageFinished-------"+url);
//html加载完成之后,添加监听图片的点击js函数
//addImageClickListener();
addImageArrayClickListener(webView);
}
/**
* android与js交互:
* 首先我们拿到html中加载图片的标签img.
* 然后取出其对应的src属性
* 循环遍历设置图片的点击事件
* 将src作为参数传给java代码
* 这个循环将所图片放入数组,当js调用本地方法时传入。
* 当然如果采用方式一获取图片的话,本地方法可以不需要传入这个数组
* 通过js代码找到标签为img的代码块,设置点击的监听方法与本地的openImage方法进行连接
* @param webView webview
*/
private void addImageArrayClickListener(WebView webView) {
webView.loadUrl("javascript:(function(){" +
"var objs = document.getElementsByTagName(\"img\"); " +
"var array=new Array(); " +
"for(var j=0;j
public class ImageJavascriptInterface {
private Context context;
private String[] imageUrls;
public ImageJavascriptInterface(Context context,String[] imageUrls) {
this.context = context;
this.imageUrls = imageUrls;
}
public ImageJavascriptInterface(Context context) {
this.context = context;
}
/**
* 接口返回的方式
*/
@android.webkit.JavascriptInterface
public void openImage(String img , String[] imageUrls) {
Intent intent = new Intent();
intent.putExtra("imageUrls", imageUrls);
intent.putExtra("curImageUrl", img);
// intent.setClass(context, PhotoBrowserActivity.class);
context.startActivity(intent);
for (int i = 0; i < imageUrls.length; i++) {
Log.e("图片地址"+i,imageUrls[i].toString());
}
}
}
if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){
//已经处于底端
}
String html = new String("我是loadData() 的标题
  我是他的内容
");
webView.loadData(html, "text/html", "UTF-8");
String html = new String("我是loadData() 的标题
  我是他的内容
");
webView.loadData(html, "text/html;charset=UTF-8", "null");