android 自带的webview控件,说实话功能已经差不多很强大了,但是实际开发中也遇到了很多的坑,这里进行一下总结,希望各位同学在使用webview控件时能少踩坑。
持续更新中…
1、支持.apk文件下载
那天我正在认真地挖坑,客户那边打电话过来说,第三方的h5页面中有一个下载客户端的按钮,点击按钮后没有反应,让我抓紧查一下问题。
经过在网上的查询,webview本身是不支持文件下载的,当然如果不嫌麻烦的话,你可以新建一个类实现DownloadListener接口,并重写onDownloadStart()来实现文件下载的操作,但是这里我想采用另一种方法,利用Intent并使用第三方应用进行下载操作。方法如下:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith(".apk")) {
Uri uri = Uri.parse(url);
Intent viewIntent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(viewIntent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
再次运行项目后,效果图:
这里需要补充说明的一点是,url可能不是以.apk结尾,
例如京东的客户端下载链接是:https://storage.360buyimg.com/build-cms/V6.2.3_jd-mloginwai.apk?timeStamp=1502247693131 ,什么,还有这种操作?.apk文件后面竟然还带了参数,这样的话,不是以.apk结尾,还是无法下载,所以比较周全的方法就把url.endsWith(“.apk”) 替换成,url.contains(“.apk”),即只要url中包含.apk,就使用第三方应用打开该链接。
2、支持https请求
Android 加载https请求的网页的时候 打不开
当load有ssl层的https页面时,如果这个网站的安全证书在android无法得到认证,WebView就会变成一个空白页,而并不会像PC浏览器中那样跳出一个风险提示框。因此,我们必须针对这种情况进行处理。(这个证书限于2.1版本以上的Android 系统才可以)
/**
* 处理ssl证书设置,用于支持https请求
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// super.onReceivedSslError(view, handler, error);//需要删除或注释掉,否则又走默认不支持https的
// handler.cancel();//表示挂起连接,为默认方式
// handler.handleMessage(null);//可做其他处理
handler.proceed();// 表示等待证书响应
}
// 这行代码一定要加上否则效果不会出现
webView.getSettings().setJavaScriptEnabled(true);
3、发送post请求
webview的loadUrl(url)方法使用的是get请求,要想通过webview发送post请求,需要调用postUrl()方法,这里要注意:post请求参数只能传byte数组,而且必须是键值对字符串形式的byte数组,其中的key是后台服务器接收的key,后台规定key是什么值就是什么值,不能随意更改,没有key=value格式,或者key不正确,都会请求不到数据,表现为网页打不开。
// postData:key=value
mWebView.postUrl(url, EncodingUtils.getBytes(postData, "UTF-8"));
4、支持本地图片上传
当html网页中input type为“file”类型的form表单,并用户点击“选择文件”按钮时,Webview向客户端提供一个回调方法(安卓各版本不一致),用于让开发人员提供图片的Uri地址,并回传到网页。
整个过程比较明了,但是由于android的系统优化升级,高版本Webview,没有保留老版本图片上传的回调方法,而是重新构造了新的回调方法。系统不是deprecated,而是直接delete了(0_0),目的是为了更好的扩展性。好吧,那我们就来做兼容。
下面就开始实现上传图片:
1.添加权限
不用多说了,必须用到的权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.CAMERA" />
2.实现自定义WebChromeClient类,并重写各版本需要的回调方法。
class MyWebChromeClient extends WebChromeClient {
// For Android 3.0-
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
if (MainActivity.mUploadMessage != null) {
MainActivity.mUploadMessage.onReceiveValue(null);
}
MainActivity.mUploadMessage = uploadMsg;
showOptions();
}
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
if (MainActivity.mUploadMessage != null) {
MainActivity.mUploadMessage.onReceiveValue(null);
}
MainActivity.mUploadMessage = uploadMsg;
showOptions();
}
// For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
if (MainActivity.mUploadMessage != null) {
MainActivity.mUploadMessage.onReceiveValue(null);
}
MainActivity.mUploadMessage = uploadMsg;
showOptions();
}
// Android 5.0+
@Override
@SuppressLint("NewApi")
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
if (MainActivity.mUploadCallbackAboveL != null) {
MainActivity.mUploadCallbackAboveL.onReceiveValue(null);
}
MainActivity.mUploadCallbackAboveL = filePathCallback;
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, MainActivity.FILECHOOSER_RESULTCODE);
return true;
}
}
public void showOptions(){
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
Intent wrapperIntent = Intent.createChooser(intent, "选择图片");
startActivityForResult(wrapperIntent, EMPView.FILECHOOSER_RESULTCODE);
}
3.选择图库
通过Intent向系统发送MediaStore.Images.Media.EXTERNAL_CONTENT_URI的图片选择请求,在Activity中会回调一个Uri,这个就是图片的地址格式类似这样:
uri=content://media/external/images/media/1393
拿到Uri之后,通过ValueCallback对象的onReceiveValue(Uri)方法,将图片地址上传成功。图片上传到服务器当然是html通过ajax请求做的,和我们没关系。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// webview文件上传
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage && null == mUploadCallbackAboveL)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (mUploadCallbackAboveL != null) {
// Android 5.0+ 执行的操作
onActivityResultAboveL(requestCode, resultCode, data);
} else if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
}
/**
* Android 5.0+ 适配
*
* @param requestCode
* @param resultCode
* @param data
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
if (requestCode != FILECHOOSER_RESULTCODE || mUploadCallbackAboveL == null) {
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
} else {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null)
results = new Uri[] { Uri.parse(dataString) };
}
}
mUploadCallbackAboveL.onReceiveValue(results);
mUploadCallbackAboveL = null;
return;
}
ok,到此就完成网页上传图片了
5、支持调起微信、支付宝支付
重写shouldOverrideUrlLoading(WebView view, String url)方法,对参数url进行判断,进行不同的处理操作,代码如下:
// 如下方案可在非微信内部WebView的H5页面中调出微信支付
if (url.startsWith("weixin://wap/pay?")) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
mContext.startActivity(intent);
return true;
}
//打开支付宝支付
if (url.contains("platformapi/startApp")) {
startAlipayActivity(url);
// android 6.0 两种方式获取intent都可以跳转支付宝成功,7.1测试不成功
} else if ((Build.VERSION.SDK_INT > 23) && (url.contains("platformapi") && url.contains("startApp"))) {
startAlipayActivity(url);
}
// 调起支付宝并跳转到指定页面
private void startAlipayActivity(String url) {
Intent intent;
try {
intent = Intent.parseUri(url,Intent.URI_INTENT_SCHEME);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setComponent(null);
mContext.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
6、Uncaught ReferenceError: functionName is not defined
网页的js代码没有加载,就调用了js方法。解决方法是在网页加载完成之后调用js方法,下面的代码是在网页加载完成后调用JS方法的实例:
myWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//在这里执行你想调用的js函数
}
});
持续更新中….
参考借鉴文章:
http://blog.csdn.net/u013210620/article/details/47184511
http://blog.csdn.net/qq_30740239/article/details/54141106
http://blog.csdn.net/u013372185/article/details/51479221
http://blog.csdn.net/qq_14859923/article/details/54375423
转载请注明出处