51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 1774|回复: 0
打印 上一主题 下一主题

Webview上传文件

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-3-6 15:55:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
要说Android中最厉害的组件莫过于Webview 了,夸张点说把这个组件放在屏幕上就可以算作一个简单地浏览器应用了。但你若认为这就万事大吉了,可太小看Webview这个磨人的妖精了,下面单就上传文件的这个坑来做展开。
从零开始

我们在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的支持:

WebSettings settings = webview.getSettings();

  1. 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+的机器了。





分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-4-25 15:49 , Processed in 0.063505 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表