Android开发深入理解WebChromeClient之onShowFileChooser或openFileChooser使用说明

2016-12-23 09:47 阅读 5,584 次 评论 0 条
版权声明:本文著作权归TeachCourse所有,未经许可禁止转载,谢谢支持!
转载请注明出处:http://teachcourse.cn/2224.html
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

摘要:

Android开发使用WebView控件加载包含表单的H5网页,点击上传文件按钮,弹出对话框,选择从相册获取照片、拍照或打开手机文件管理器,从Android手机选取一张图片或一个文件,然后通过ValueCallback接口传递,在WebView加载的H5网页显示。这里有一个问题,点击“取消”或返回按钮,无法重复回调onShowFileChooser或openFileChooser方法,控制台打印:Attempted to finish an input event but the input event receiver has already been disposed

一、深入理解onShowFileChooser或openFileChooser

上一篇文章TeachCourse详细分析WebChromeClient各个方法的使用,特殊说明关于WebChromeClient,它既不是接口也不是抽象类,但声明的方法很多方法体都是空的,这是让钊林感到疑惑之一,查看WebView源码,setWebChromeClient()传入WebChromeClient对象,然后使用传入的对象,调用WebChromeClient声明的方法,再将一些参数传递返回WebChromeClient空方法体重。在WebView源码里面代码也很简单,详细的处理处理逻辑看不到,这是让钊林感到疑惑之二,感觉像一个黑箱子。

然后就一直想,那么重写WebChromeClient的方法有什么作用呢?先看一下onShowFileChooser,如下:

  1. /** 
  2.  * Tell the client to show a file chooser. 
  3.  * 
  4.  * This is called to handle HTML forms with 'file' input type, in response to the 
  5.  * user pressing the "Select File" button. 
  6.  * To cancel the request, call <code>filePathCallback.onReceiveValue(null)</code> and 
  7.  * return true. 
  8.  * 
  9.  * @param webView The WebView instance that is initiating the request. 
  10.  * @param filePathCallback Invoke this callback to supply the list of paths to files to upload, 
  11.  *                         or NULL to cancel. Must only be called if the 
  12.  *                         <code>showFileChooser</code> implementations returns true. 
  13.  * @param fileChooserParams Describes the mode of file chooser to be opened, and options to be 
  14.  *                          used with it. 
  15.  * @return true if filePathCallback will be invoked, false to use default handling. 
  16.  * 
  17.  * @see FileChooserParams 
  18.  */  
  19. public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,  
  20.         FileChooserParams fileChooserParams) {  
  21.     return false;  
  22. }  

该方法的作用,告诉当前APP,打开一个文件选择器,比如:打开相册、启动拍照或打开本地文件管理器,实际上更好的理解,WebView加载包含上传文件的表单按钮,HTML定义了input标签,同时input的type类型为file,手指点击该按钮,回调onShowFileChooser这个方法,在这个重写的方法里面打开相册、启动照片或打开本地文件管理器,甚至做其他任何的逻辑处理,点击一次回调一次的前提是请求被取消,而取消该请求回调的方法:给ValueCallback接口的onReceiveValue抽象方法传入null,同时onShowFileChooser方法返回true;

ValueCallback的抽象方法被回调onShowFileChooser方法返回true;反之返回false;再来看一下openFileChooser的源码,如下:

  1. /** 
  2.  * Tell the client to open a file chooser. 
  3.  * @param uploadFile A ValueCallback to set the URI of the file to upload. 
  4.  *      onReceiveValue must be called to wake up the thread.a 
  5.  * @param acceptType The value of the 'accept' attribute of the input tag 
  6.  *         associated with this file picker. 
  7.  * @param capture The value of the 'capture' attribute of the input tag 
  8.  *         associated with this file picker. 
  9.  * 
  10.  * @deprecated Use {@link #showFileChooser} instead. 
  11.  * @hide This method was not published in any SDK version. 
  12.  */  
  13. @SystemApi  
  14. @Deprecated  
  15. public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {  
  16.     uploadFile.onReceiveValue(null);  
  17. }  

在所有发布的SDK版本中,openFileChooser是一个隐藏的方法,使用onShowFileChooser代替,但是最好同时重写showFileChooser和openFileChooser方法,Android 4.4.X以上的系统回调onShowFileChooser方法,低于或等于Android 4.4.X的系统回调openFileChooser方法,只重写onShowFileChooser或openFileChooser造成在有的系统可以正常回调,在有的系统点击没有反应。

仔细分析onShowFileChooser和openFileChooser回调方法,这两个方法之间的区别,

第一个区别:前者ValueCallback接口回传一个Uri数组,后者回传一个Uri对象,在onActivityResult回调方法中调用ValueCallback接口方法onReceiveValue传入参数特别注意;

  1. /** 
  2.  *回调onShowFileChooser方法,onReceiveValue传入Uri对象数组 
  3.  */  
  4. mFilePathCallback.onReceiveValue(new Uri[]{uri});  
  1. /** 
  2.  *回调openFileChooser方法,onReceiveValue传入一个Uri对象 
  3.  */  
  4. mFilePathCallback4.onReceiveValue(uri);  

第二个区别:前者将后者的acceptType、capture封装成FileChooserParams抽象类

二、实例展示onShowFileChooser或openFileChooser处理过程

1223-0941-popupdialog-2
这是实例运行的效果图,H5表单写入两个上传文件的按钮,点击其中一个从底部弹出对话框,选择相册文件或拍照,点击“取消”按钮,再次点击“上传文件”按钮能够再次回调onShowFileChooser或openFileChooser方法。

在之前的理解中,误解onShowFileChooser或openFileChooser只能打开相册或启动相机拍照,其实不仅仅是这样,onShowFileChooser或openFileChooser既然是一个回调的方法,可以重复执行各种逻辑代码,比如:启动另一个Activity、弹窗对话框、录制视频或录音等

在上面的例子中,执行弹窗操作,将弹窗的处理代码放置onShowFileChooser或openFileChooser方法体,如下:

  1. @Override  
  2. public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {  
  3.     super.onShowFileChooser(webView, filePathCallback, fileChooserParams);  
  4.   
  5.          popupDialog();  
  6.     PickPhotoUtil.mFilePathCallback = filePathCallback;  
  7.     /** 
  8.      * 返回true,如果filePathCallback被调用;返回false,如果忽略处理 
  9.      */  
  10.     return true;  
  11. }  

  1. public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {  
  2.   
  3.              popupDialog();  
  4.         String title = acceptType;  
  5.         PickPhotoUtil.mFilePathCallback4 = filePathCallback;  
  6.     }  

点击弹窗取消按钮、点击打开相册取消操作或取消拍照,可能无法再次回调onShowFileChooser或openFileChooser方法,如果你没有在点击弹窗取消方法中或onActivityResult回调方法resultCode==RESULT_CANCELED处理,再次点击上传按钮,打印出log:

Attempted to finish an input event but the input event receiver has already been disposed

同时,点击没有效果

  1. /** 
  2.      * 弹窗,启动拍照或打开相册 
  3.      */  
  4.     public void popupDialog() {  
  5.         ActionSheetDialog actionSheetDialog= new ActionSheetDialog(activity).builder()  
  6.                 .setCancelable(false)  
  7.                 .setCanceledOnTouchOutside(false)  
  8.                 .addSheetItem("手机拍照", ActionSheetDialog.SheetItemColor.Blue,  
  9.                         new ActionSheetDialog.OnSheetItemClickListener() {  
  10.                             @Override  
  11.                             public void onClick(int which) {  
  12.                                 goToTakePhoto();  
  13.                             }  
  14.                         })  
  15.                 .addSheetItem("手机相册", ActionSheetDialog.SheetItemColor.Blue,  
  16.                         new ActionSheetDialog.OnSheetItemClickListener() {  
  17.                             @Override  
  18.                             public void onClick(int which) {  
  19.                                 goForPicFile();  
  20.                             }  
  21.                         });  
  22.         actionSheetDialog.show();  
  23.         /** 
  24.          * 设置点击“取消”按钮监听,目的取消mFilePathCallback回调,可以重复调起弹窗 
  25.          */  
  26.         actionSheetDialog.setOnClickListener(new View.OnClickListener() {  
  27.             @Override  
  28.             public void onClick(View v) {  
  29.                 cancelFilePathCallback();  
  30.             }  
  31.         });  
  32.     }  
  1. /** 
  2.      * onActivityResult回调方法,当resultCode==RESULT_CANCELED,取消mFilePathCallback回调,可以* 重复调起弹窗 
  3.      */  
  4.         @Override  
  5.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  6.         super.onActivityResult(requestCode, resultCode, data);  
  7.         switch (requestCode) {  
  8.         /** 
  9.          *打开系统自带文件管理器回调 
  10.          */  
  11.             case PickPhotoUtil.REQUEST_FILE_PICKER:  
  12.                 pickPhotoResult(resultCode, data);  
  13.   
  14.                 break;  
  15.         /** 
  16.          *打开相册回调 
  17.          */  
  18.             case PickPhotoUtil.REQUEST_CODE_PICK_PHOTO:  
  19.                 pickPhotoResult(resultCode, data);  
  20.   
  21.                 break;  
  22.         /** 
  23.          *拍照后回调 
  24.          */  
  25.             case PickPhotoUtil.REQUEST_CODE_TAKE_PHOTO:  
  26.                 takePhotoResult(resultCode);  
  27.   
  28.                 break;  
  29.             default:  
  30.                 break;  
  31.         }  
  32.     }  
  1. /** 
  2.      *取消mFilePathCallback回调 
  3.      */  
  4.     private void cancelFilePathCallback() {  
  5.         if (PickPhotoUtil.mFilePathCallback4 != null) {  
  6.             PickPhotoUtil.mFilePathCallback4.onReceiveValue(null);  
  7.             PickPhotoUtil.mFilePathCallback4 = null;  
  8.         } else if (PickPhotoUtil.mFilePathCallback != null) {  
  9.             PickPhotoUtil.mFilePathCallback.onReceiveValue(null);  
  10.             PickPhotoUtil.mFilePathCallback = null;  
  11.         }  
  12.     }  

在不期待回调mFilePathCallback的onReceiveValue方法时,调用cancelFilePathCallback(),解决点击上传按钮无法重复回调的问题。

ps:

Demo已上传GitHub,路径view\webview\UploadImgForH5Activity.java

关注公众号 扫一扫二维码,加我QQ

如果文章对你有帮助,欢迎点击上方按钮关注作者

来源:TeachCourse每周一次,深入学习Android教程,关注(QQ1589359239或公众号TeachCourse)
转载请注明出处:http://teachcourse.cn/2224.html

资源分享

Demo源码下载
Android应用微信登录功能实现 Android应用微信登录功能实现
Android学习笔记六:Java基础知识 Android学习笔记六:Java基础知
深入理解接口的定义和意义(2)之MVP实例 深入理解接口的定义和意义(2)之
生活杂谈之万网域名注册过程详解 生活杂谈之万网域名注册过程详解

发表评论

呲牙 憨笑 坏笑 偷笑 色 微笑 抓狂 睡觉 酷 流汗 鼓掌 大哭 可怜 疑问 晕 惊讶 得意 尴尬 发怒 奋斗 衰 骷髅 啤酒 吃饭 礼物 强 弱 握手 OK NO 勾引 拳头 差劲 爱你

表情