zoukankan      html  css  js  c++  java
  • WebView 上传文件 WebChromeClient之openFileChooser函数

    原链接:http://blog.saymagic.cn/2015/11/08/webview-upload.html?utm_source=tuicool&utm_medium=referral

    从零开始

    我们在xml中写入一个简单的Webview组件:

    1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    3. android:layout_height="match_parent"
    4. tools:context=".MainActivity">
    5. <WebView
    6. android:id="@+id/webview"
    7. android:layout_width="fill_parent"
    8. android:layout_height="fill_parent"
    9. android:layout_margin="5dp"></WebView>
    10. </RelativeLayout>

    然后在Java代码中使用其加载一个能够提供上传服务的URL:

    1. WebView webview = (WebView) findViewById(R.id.webview);
    2. webview.loadUrl(A_UPLOAD_URL);

    之后,要加网络权限:

    1. <uses-permission android:name="android.permission.INTERNET"></uses-permission>

    如果想让Webview能够访问本地资源,SD卡的读写权限也是避免不了的:

    1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

    最后,我们运行,会发现根本不能访问本地资源。Why?

    让我们来填补第一个坑:

    支持上传文件

    Webview执行上传操作的逻辑是这样的:首先准备上传时会回调WebChromeClient类下的openFileChooser方法,在这个方法中给我们机会发起Intent来打开支持提供文件的第三方应用,最后在onActivityResult回调中将第三方应用提供的内容通过一个叫做ValueCallback的参数返回给Webview(详细点来说:ValueCallback是在openFileChooser 方法里由webview提供给我们的,里面包裹一个Uri,我们在onActivityResult 里将选中的Uri反馈给ValueCallback,这时候相当于Webview就知道我们选择了什么文件),因此,我们需要为Webview设置一个提供openFileChooser方法的WebChromeClient,这个方法在不同版本的Android中参数是不同的,为此我们一般需要写三个重载函数,大致像这个样子:

    1. private ValueCallback<Uri> mUploadMessage;
    2. //设置`WebChromeClient`:
    3. webview.setWebChromeClient(new WebChromeClient(){
    4. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
    5. Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg)");
    6. mUploadMessage = uploadMsg;
    7. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    8. i.addCategory(Intent.CATEGORY_OPENABLE);
    9. i.setType("*/*");
    10. MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    11. }
    12. public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
    13. Log.d(TAG, "openFileChoose( ValueCallback uploadMsg, String acceptType )");
    14. mUploadMessage = uploadMsg;
    15. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    16. i.addCategory(Intent.CATEGORY_OPENABLE);
    17. i.setType("*/*");
    18. MainActivity.this.startActivityForResult(
    19. Intent.createChooser(i, "File Browser"),
    20. FILECHOOSER_RESULTCODE);
    21. }
    22. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
    23. Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");
    24. mUploadMessage = uploadMsg;
    25. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    26. i.addCategory(Intent.CATEGORY_OPENABLE);
    27. i.setType("*/*");
    28. MainActivity.this.startActivityForResult( Intent.createChooser( i, "File Browser" ), MainActivity.FILECHOOSER_RESULTCODE );
    29. }
    30. });
    31. //onActivityResult回调
    32. @Override
    33. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    34. super.onActivityResult(requestCode, resultCode, data);
    35. if(requestCode==FILECHOOSER_RESULTCODE)
    36. {
    37. if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
    38. Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
    39. if (mUploadMessage != null) {
    40. mUploadMessage.onReceiveValue(result);
    41. mUploadMessage = null;
    42. }
    43. }
    44. }

    还有重要的一点:如果这个上传操作涉及到JS操作,别忘记对Webview开启对JS的支持:

    1. WebSettings settings = webview.getSettings();
    2. settings.setJavaScriptEnabled(true);

    这样,打个debug包测试看以下,不出意外我们的Webview应该可以支持上传操作了。

    别高兴得太早,如果这个时候产品要将release包推向市场,当你把release包交给产品时,你会发现你的Webview又不能上传了,什么情况?

    请听Webview上传操作的第二个坑。

    支持release版

    debug版是好的,为什么release就不行了呢?准确的说,开启了混淆的release包是不可以的,究其原因在于,openFileChooser 方法并不是WebChromeClient 的对外开放的方法,因此这个方法会被混淆,解决办法也比较简单,只需要在混淆文件里控制一下即可:

    1. -keepclassmembers class * extends android.webkit.WebChromeClient{
    2. public void openFileChooser(...);
    3. }

    好了,我们的Webview可以作为应用内的一个部分对外发布了,等等,有5.0以上用户反映用不了?纳尼????

    别回心,来看看这第三个坑。

    支持5.0

    在5.0发布后,Android人家说了,这次我们回调的不是openFileChooser方法,而是onShowFileChooser方法,并且上文提到的ValueCallback参数里包裹着不再是Uri,而是Uri数组,因此我们必须为5.0+的机器做适配,大致思路如下:

    1. webview.setWebChromeClient(new WebChromeClient(){
    2. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
    3. ...
    4. }
    5. public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
    6. ...
    7. }
    8. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
    9. ...
    10. }
    11. // For Android 5.0+
    12. public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
    13. mUploadCallbackAboveL = filePathCallback;
    14. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    15. i.addCategory(Intent.CATEGORY_OPENABLE);
    16. i.setType("*/*");
    17. MainActivity.this.startActivityForResult(
    18. Intent.createChooser(i, "File Browser"),
    19. FILECHOOSER_RESULTCODE);
    20. return true;
    21. }
    22. });
    23. @Override
    24. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    25. super.onActivityResult(requestCode, resultCode, data);
    26. if(requestCode==FILECHOOSER_RESULTCODE)
    27. {
    28. if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
    29. Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
    30. if (mUploadCallbackAboveL != null) {
    31. onActivityResultAboveL(requestCode, resultCode, data);
    32. }
    33. else if (mUploadMessage != null) {
    34. mUploadMessage.onReceiveValue(result);
    35. mUploadMessage = null;
    36. }
    37. }
    38. }
    39. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    40. private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
    41. if (requestCode != FILECHOOSER_RESULTCODE
    42. || mUploadCallbackAboveL == null) {
    43. return;
    44. }
    45. Uri[] results = null;
    46. if (resultCode == Activity.RESULT_OK) {
    47. if (data == null) {
    48. } else {
    49. String dataString = data.getDataString();
    50. ClipData clipData = data.getClipData();
    51. if (clipData != null) {
    52. results = new Uri[clipData.getItemCount()];
    53. for (int i = 0; i < clipData.getItemCount(); i++) {
    54. ClipData.Item item = clipData.getItemAt(i);
    55. results[i] = item.getUri();
    56. }
    57. }
    58. if (dataString != null)
    59. results = new Uri[]{Uri.parse(dataString)};
    60. }
    61. }
    62. mUploadCallbackAboveL.onReceiveValue(results);
    63. mUploadCallbackAboveL = null;
    64. return;
    65. }

    如上,我们的Webview应该就可以适应5.0+的机器了。

  • 相关阅读:
    SQLSERVER跨库访问
    Mybatis开发的几个主要事项
    jqGrid参数
    WPF 从当前层次遍历查找 子控件及父控件
    c# 获取图像像素
    异步FIFO的FPGA实现
    note5文档流
    note3css 的padding属性
    note3clip:rect('top', 'right', 'bottom', 'left')是什么意思
    怎样查看端口被占用情况
  • 原文地址:https://www.cnblogs.com/lianghui66/p/6004211.html
Copyright © 2011-2022 走看看