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方法:


  
  
  1. private void onReceiveImage(final Intent intent, final ValueCallback<Uri> filePathCallback, final ValueCallback<Uri[]> filePathCallbacks) {
  2. Uri imageUri = null;
  3. String image = intent.getStringExtra( "cropImageUri");
  4. if (!TextUtils.isEmpty(image)) {
  5. imageUri = Uri.parse(image);
  6. }
  7. if(filePathCallback != null) {
  8. filePathCallback.onReceiveValue(imageUri);
  9. }
  10. if(filePathCallbacks != null) {
  11. if(imageUri != null) {
  12. filePathCallbacks.onReceiveValue( new Uri[]{imageUri});
  13. } else {
  14. filePathCallbacks.onReceiveValue( null);
  15. }
  16. }
  17. }



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

而整个ValueCallBack会被挂起,也就是说,此次回调一直在等待回调结果,但是一直等不到结果,所以后面再有请求触发就会一直得不到响应,所以导致出现了这个问题。
  
  
那我们需要做的就是,当用户并没有选择文件的时候,我们需要手动结束调此次回调过程,避免请求被挂起。
  
  
 
if (resultCode==Activity.RESULT_CANCELED){ if (mOpenFileWebChromeClient.mFilePathCallbacks!=null){ //xie :直接点击取消时,ValueCallback回调会被挂起,需要手动结束掉回调,否则再次点击选择照片无响应 mOpenFileWebChromeClient.mFilePathCallbacks.onReceiveValue(null); mOpenFileWebChromeClient.mFilePathCallbacks=null; } }
  
  


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

 
转自:https://blog.csdn.net/xieluoxixi/article/details/77049736

猜你喜欢

转载自blog.csdn.net/u013651026/article/details/88818228