最近一直都在做与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方法:
扫描二维码关注公众号,回复:
5742858 查看本文章
-
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的原因呢。
转自:https://blog.csdn.net/xieluoxixi/article/details/77049736