WebView调H5上传文件点击取消时无法再次响应H5上的选择文件事件

最近一直都在做与H5交互的事情,算是踩了好多坑吧,再加上个人原因好长时间没更博了,再次回到状态,于是乎更了这篇博客。

项目中有需求WebView加载H5上页面,然后响应H5上的上传文件事件,由于安卓无法像IOS的那样直接调系统原生的接口,所以只能自己封装方法,再加上android M之后运行时权限的问题,像选文件或者拍照,这种敏感的事件都需要单独处理,总之好多坑。

代码写完之后,测试的时候偶然发现的BUG还真难解。

 

H5上上传文件协议为:

// <input type="file" name="fileField" id="fileField" />

 

在安卓这边我自己封装了从图库选图或者拍照,选择完文件之后,需要回显在H5上,这些并不麻烦,WebView为我们提供

WebChromeClient 这个类,拓展这个类,然后复写里面的方法,处理起来并不是太麻烦,在此可能需要对不同版本的安卓手机做不同的处理:
我代码如下:

 
public class OpenFileWebChromeClient extends WebChromeClient { public static final int REQUEST_FILE_PICKER = 1; public ValueCallback<Uri> mFilePathCallback; public ValueCallback<Uri[]> mFilePathCallbacks; Activity mContext; @Override public void onReceivedTitle(WebView webView, String s) { super.onReceivedTitle(webView, s); mCenterTitle.setText(s); } public OpenFileWebChromeClient(Activity mContext) { super(); this.mContext = mContext; } // Android < 3.0 调用这个方法 public void openFileChooser(final ValueCallback<Uri> filePathCallback) { mFilePathCallback = filePathCallback; takeOrPickPicture(); } // 3.0 + 调用这个方法 public void openFileChooser(final ValueCallback filePathCallback, final String acceptType) { mFilePathCallback = filePathCallback; takeOrPickPicture(); } // js上传文件的<input type="file" name="fileField" id="fileField" />事件捕获 // Android > 4.1.1 调用这个方法 public void openFileChooser(final ValueCallback<Uri> filePathCallback, final String acceptType, final String capture) { mFilePathCallback = filePathCallback; takeOrPickPicture(); } @Override public boolean onShowFileChooser(final WebView webView, final ValueCallback<Uri[]> filePathCallback, final WebChromeClient.FileChooserParams fileChooserParams) { mFilePathCallbacks = filePathCallback; takeOrPickPicture(); return true; } private void takeOrPickPicture() { //系统选照片 //Intent intent = new Intent(Intent.ACTION_GET_CONTENT); //intent.addCategory(Intent.CATEGORY_OPENABLE); //intent.setType("*/*"); //mContext.startActivityForResult(Intent.createChooser(intent, "File Chooser"), REQUEST_FILE_PICKER); Intent intent = new Intent(ResumeEditActivity.this, SelectPhotoFromActivity.class); mContext.startActivityForResult(intent, REQUEST_FILE_PICKER); } } } 


takeOrPickPicture()调用的是自己封装的拍照选图的操作,然后给webview设置WebChromeClient之后选图操作完成


 
mWebView.setWebChromeClient(mOpenFileWebChromeClient);

 

在H5上回显则需要在当前Activity里面复写onActivityResult回调方法

代码如下:

 
/** * 以下代码是为了适应H5调用本地图片并且显示在h5上 */ @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == OpenFileWebChromeClient.REQUEST_FILE_PICKER && resultCode == Activity.RESULT_OK) { /* if (mOpenFileWebChromeClient.mFilePathCallback != null) { Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData(); if (result != null) { String path = ProviderPathUtils.getPath(this, result); Uri uri = Uri.fromFile(new File(path)); mOpenFileWebChromeClient.mFilePathCallback.onReceiveValue(uri); } else { mOpenFileWebChromeClient.mFilePathCallback.onReceiveValue(null); } } if (mOpenFileWebChromeClient.mFilePathCallbacks != null) { Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData(); if (result != null) { String path = ProviderPathUtils.getPath(this, result); Uri uri = Uri.fromFile(new File(path)); mOpenFileWebChromeClient.mFilePathCallbacks.onReceiveValue(new Uri[]{uri}); } else { mOpenFileWebChromeClient.mFilePathCallbacks.onReceiveValue(null); } } */ onReceiveImage(intent, mOpenFileWebChromeClient.mFilePathCallback, mOpenFileWebChromeClient.mFilePathCallbacks); mOpenFileWebChromeClient.mFilePathCallback = null; mOpenFileWebChromeClient.mFilePathCallbacks = null; }else if (resultCode==Activity.RESULT_CANCELED){ if (mOpenFileWebChromeClient.mFilePathCallbacks!=null){ //xie :直接点击取消时,ValueCallback回调会被挂起,需要手动结束掉回调,否则再次点击选择照片无响应 mOpenFileWebChromeClient.mFilePathCallbacks.onReceiveValue(null); mOpenFileWebChromeClient.mFilePathCallbacks=null; } } } 

onReceiveImage方法:

 

private void onReceiveImage(final Intent intent, final ValueCallback<Uri> filePathCallback, final ValueCallback<Uri[]> filePathCallbacks) {
        Uri imageUri = null;
        String image = intent.getStringExtra("cropImageUri");
        if (!TextUtils.isEmpty(image)) {
            imageUri = Uri.parse(image);
        }
        if(filePathCallback != null) {
            filePathCallback.onReceiveValue(imageUri);
        }
        if(filePathCallbacks != null) {
            if(imageUri != null) {
                filePathCallbacks.onReceiveValue(new Uri[]{imageUri});
            }else {
                filePathCallbacks.onReceiveValue(null);
            }
        }
    }



至此整个过程处理完毕,我来分析下,开篇提到的BUG,其实还是因为粗心所致,问题是这样的,我们选图时会回调ValueCallBack,但是恰巧此时你并没有选图而是直接点击取消

而整个ValueCallBack会被挂起,也就是说,此次回调一直在等待回调结果,但是一直等不到结果,所以后面再有请求触发就会一直得不到响应,所以导致出现了这个问题。
那我们需要做的就是,当用户并没有选择文件的时候,我们需要手动结束调此次回调过程,避免请求被挂起。

 
if (resultCode==Activity.RESULT_CANCELED){ if (mOpenFileWebChromeClient.mFilePathCallbacks!=null){ //xie :直接点击取消时,ValueCallback回调会被挂起,需要手动结束掉回调,否则再次点击选择照片无响应 mOpenFileWebChromeClient.mFilePathCallbacks.onReceiveValue(null); mOpenFileWebChromeClient.mFilePathCallbacks=null; } }


方法总比困难多,BUG并不怕,怕的是找不到出BUG的原因呢。


 

 

 

 

 

 

展开阅读全文
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值