Android webview 踩坑经验总结

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

转载请注明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值