解决WebView文件上传无法重复选择问题

Android 开发使用WebView控件加载包含表单的 H5 网页,点击上传文件按钮,弹出对话框,选择从相册获取照片、拍照或打开手机文件管理器,从 Android 手机选取一张图片或一个文件,然后通过ValueCallback接口传递,在 WebView 加载的 H5网页 显示。

这里有一个问题,点击“取消”或返回按钮,无法重复回调onShowFileChooseropenFileChooser方法,控制台打印:

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

一、深入理解onShowFileChooser或openFileChooser

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

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

/**
     * Tell the client to show a file chooser.
     *
     * This is called to handle HTML forms with 'file' input type, in response to the
     * user pressing the "Select File" button.
     * To cancel the request, call <code>filePathCallback.onReceiveValue(null)</code> and
     * return {@code true}.
     *
     * @param webView The WebView instance that is initiating the request.
     * @param filePathCallback Invoke this callback to supply the list of paths to files to upload,
     *                         or {@code null} to cancel. Must only be called if the
     *                         {@link #onShowFileChooser} implementation returns {@code true}.
     * @param fileChooserParams Describes the mode of file chooser to be opened, and options to be
     *                          used with it.
     * @return {@code true} if filePathCallback will be invoked, {@code false} to use default
     *         handling.
     *
     * @see FileChooserParams
     */
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
            FileChooserParams fileChooserParams) {
        return false;
    }

 

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

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

 /**
     * Tell the client to open a file chooser.
     * @param uploadFile A ValueCallback to set the URI of the file to upload.
     *      onReceiveValue must be called to wake up the thread.a
     * @param acceptType The value of the 'accept' attribute of the input tag
     *         associated with this file picker.
     * @param capture The value of the 'capture' attribute of the input tag
     *         associated with this file picker.
     *
     * @deprecated Use {@link #onShowFileChooser} instead.
     * @hide This method was not published in any SDK version.
     */
    @SystemApi
    @Deprecated
    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
        uploadFile.onReceiveValue(null);
    }

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

仔细分析onShowFileChooseropenFileChooser回调方法,这两个方法之间的区别:

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

/** 
 *回调onShowFileChooser方法,onReceiveValue传入Uri对象数组 
 */  
mFilePathCallback.onReceiveValue(new Uri[]{uri});  

/** 
 *回调openFileChooser方法,onReceiveValue传入一个Uri对象 
 */  
mFilePathCallback4.onReceiveValue(uri);  

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

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

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

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

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

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

public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {  
  
             popupDialog();  
        String title = acceptType;  
        PickPho

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

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

同时,点击没有效果。解决方案:

 

 

/** 
     * 弹窗,启动拍照或打开相册 
     */  
    public void popupDialog() {  
        ActionSheetDialog actionSheetDialog= new ActionSheetDialog(activity).builder()  
                .setCancelable(false)  
                .setCanceledOnTouchOutside(false)  
                .addSheetItem("手机拍照", ActionSheetDialog.SheetItemColor.Blue,  
                        new ActionSheetDialog.OnSheetItemClickListener() {  
                            @Override  
                            public void onClick(int which) {  
                                goToTakePhoto();  
                            }  
                        })  
                .addSheetItem("手机相册", ActionSheetDialog.SheetItemColor.Blue,  
                        new ActionSheetDialog.OnSheetItemClickListener() {  
                            @Override  
                            public void onClick(int which) {  
                                goForPicFile();  
                            }  
                        });  
        actionSheetDialog.show();  
        /** 
         * 设置点击“取消”按钮监听,目的取消mFilePathCallback回调,可以重复调起弹窗 
         */  
        actionSheetDialog.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                cancelFilePathCallback();  
            }  
        });  
    }  
/** 
     * onActivityResult回调方法,当resultCode==RESULT_CANCELED,取消mFilePathCallback回调,可以* 重复调起弹窗 
     */  
        @Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        super.onActivityResult(requestCode, resultCode, data);  
        switch (requestCode) {  
        /** 
         *打开系统自带文件管理器回调 
         */  
            case PickPhotoUtil.REQUEST_FILE_PICKER:  
                pickPhotoResult(resultCode, data);  
  
                break;  
        /** 
         *打开相册回调 
         */  
            case PickPhotoUtil.REQUEST_CODE_PICK_PHOTO:  
                pickPhotoResult(resultCode, data);  
  
                break;  
        /** 
         *拍照后回调 
         */  
            case PickPhotoUtil.REQUEST_CODE_TAKE_PHOTO:  
                takePhotoResult(resultCode);  
  
                break;  
            default:  
                break;  
        }  
    }  
/** 
     *取消mFilePathCallback回调 
     */  
    private void cancelFilePathCallback() {  
        if (PickPhotoUtil.mFilePathCallback4 != null) {  
            PickPhotoUtil.mFilePathCallback4.onReceiveValue(null);  
            PickPhotoUtil.mFilePathCallback4 = null;  
        } else if (PickPhotoUtil.mFilePathCallback != null) {  
            PickPhotoUtil.mFilePathCallback.onReceiveValue(null);  
            PickPhotoUtil.mFilePathCallback = null;  
        }  
    }  

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebView无法下载的问题一般是因为没有设置相应的下载配置,可以尝试以下解决方法: 1. 在 AndroidManifest.xml 中添加权限: ```xml <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ``` 2. 设置 WebChromeClient,并重写 onDownloadStart 方法: ```java WebView webView = findViewById(R.id.webView); webView.setWebChromeClient(new WebChromeClient() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); request.setMimeType(mimetype); String cookies = CookieManager.getInstance().getCookie(url); request.addRequestHeader("cookie", cookies); request.addRequestHeader("User-Agent", userAgent); request.setDescription("Downloading file..."); request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimetype)); request.allowScanningByMediaScanner(); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, contentDisposition, mimetype)); DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); dm.enqueue(request); Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show(); } }); ``` 重写 onDownloadStart 方法后,点击 WebView 中的下载链接时,会调用该方法进行下载。 注意:需要在 AndroidManifest.xml 中添加 DOWNLOAD_SERVICE 权限。 ```xml <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值