最近在写一个react native 项目,其中react-native-webview库一些使用着实遇到了不少问题,耗时比较长,现在和大家分享一下。

图片上传时选择拍照是很常见的功能,写的h5项目一直调用正常。使用方式大概如下:

<input
          type="file"
          ref={inputRef}
          accept="image/*"
          capture={'camera' as any}
          id="uploadImage"
          onChange={handleUploadChange}
        />

在H5下这段代码是正常可以使用的,调起相机和图库选择器,但是使用react-native-webview要注意。

首先要在AndroidManifest.xml注入权限,大概是如下三个,这个没很认真调研过,有错误可以提出。

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

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

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

        权限注入了之后,我认为一切都可以了,没想到这才是坑的开始。相机死活调不出来,经过查资料,大概有以下几种说法:

  • 1、原生webview不支持input accept="image",capture="camera"这种方式(原生是真的不支持)。但我使用的是类库react-native-webview,我个人以为是一个问题,然后才被坑下去的。
  • 2、xml权限注入的不对,然后各种尝试+各种查哪些权限是干啥用的。
  • 3、react-native 版本有问题
  • 4、react-native-webview版本有问题

并且在查的过程中还发生一些其他的血案,分享个地址给大家看下:https://github.com/react-native-webview/react-native-webview/issues/2679

官网在介绍如果input调不起相机的话,可以去这个地址看一下:react-native-webview/Guide.md at master · react-native-webview/react-native-webview · GitHub

然后呢?

import { WebView } from "react-native-webview";

WebView.isFileUploadSupported().then(res => {
  if (res === true) {
    // file upload is supported
  } else {
    // not file upload support
  }
});

         这是代码,然后,就报错了,遇到了issues/2679这个问题,在这个坑里挣扎了半天,看node-modules导入的源码,并没有找到WebView.isFileUploadSupported这个方法,到最后我也没找到,这只不过是其中的一个弯路而已,最后我放弃了这个。

        因为没找到问题,我就找到了另外一篇文章,是教咱怎么在原生种实现webview调起相机的功能的。然后我就打算给react-native-webview原生实现一个选择相机的功能,地址链接:Android WebView支持input file启用相机/选取照片功能 - 腾讯云开发者社区-腾讯云

        开干,然后在这个坑里爬了半天,基本上把 react-native-webview源码扒了个遍(PS:当然我不是安卓开发,不太懂),然后找到了其中一个文件:

Android 调用相机拍照后取不到文件_react.js

这个文件包含了点击input 相关的调用逻辑,不是很复杂,主要代码就这段:

if (!needsCameraPermission()) {
      if (acceptsImages(acceptTypes)) {
        Intent photoIntent = getPhotoIntent();
        if (photoIntent != null) {
          extraIntents.add(photoIntent);
        }
      }
      if (acceptsVideo(acceptTypes)) {
        Intent videoIntent = getVideoIntent();
        if (videoIntent != null) {
          extraIntents.add(videoIntent);
        }
      }
    }

        这段代码才是下一个坑的开始,因为先入为主的原因,我以为react-native-webview根本就没实现相机的调用,于是我就结合了顶上那篇文章,开始了修改源码的爬坑之旅,由于没有java的经验,折腾了一个多小时后,终于相机出来了😭。

        然后下一个坑出来,还是因为先入为主。点击相机没反应?然后各种查资料,看代码,才发现,getPhotoIntent这个方法:

private Intent getPhotoIntent() {
    Intent intent = null;

    try {
      outputImage = getCapturedFile(MimeType.IMAGE);
      Uri outputImageUri = getOutputUri(outputImage);
      intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, outputImageUri);
    } catch (IOException | IllegalArgumentException e) {
      Log.e("CREATE FILE", "Error occurred while creating the File", e);
      e.printStackTrace();
    }

    return intent;
  }

TMD,这不就是添加相机的地方吗?以为是这个方法的问题导致相机没有显示:acceptsImages(acceptType),后来发现发现不是,

private Boolean acceptsImages(String types) {
    String mimeType = types;
    if (types.matches("\\.\\w+")) {
      mimeType = getMimeTypeFromExtension(types.replace(".", ""));
    }
    return mimeType.isEmpty() || mimeType.toLowerCase().contains(MimeType.IMAGE.value);
  }

然后各种找,先入为主害死人啊!添加相机选项的顶上,还有个needsCameraPermission方法,这个方法是获取权限的,我一直以为权限是有的,然后,打断点,发现,这里并没有获取到想要的权限。并且查看手机系统设置-》应用里面的相机权限是禁用,在设置里面打开权限之后,相机调用也正常了,然后我就EMO了。

很多文章告诉我们,权限并不需要手动获取,只需要加到配置清单中就行了,但实际并不是,相机这种权限还是需要主要跟用户获取的,当然用户同意了之后,以后就不需要再次获取了。

最终解决方案,大家可以看一下这篇文章,因为我的页面是需要相机权限的,所以我一进入这个页面就发起了权限的获取。React-Native之Android(6.0及以上)权限申请详解 - 腾讯云开发者社区-腾讯云

示例代码如下:

//请求多个权限
  const requestMultiplePermission = async () => {
    try {
      const permissions = [
        PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
        PermissionsAndroid.PERMISSIONS.CAMERA,
      ];
      //返回得是对象类型
      const granteds = await PermissionsAndroid.requestMultiple(permissions);
      var data = '是否同意地址权限: ';
      if (granteds['android.permission.ACCESS_FINE_LOCATION'] === 'granted') {
        data = data + '是\n';
      } else {
        data = data + '否\n';
      }
      data = data + '是否同意相机权限: ';
      if (granteds['android.permission.CAMERA'] === 'granted') {
        data = data + '是\n';
      } else {
        data = data + '否\n';
      }
      data = data + '是否同意存储权限: ';
      if (granteds['android.permission.WRITE_EXTERNAL_STORAGE'] === 'granted') {
        data = data + '是\n';
      } else {
        data = data + '否\n';
      }
    } catch (err) {
      // this.show(err.toString());
    }
  };

  useEffect(() => {
    requestMultiplePermission();
  }, []);

这段代码会在用户进入页面时发起一次权限的获取,如果已经有权限了,就不会再次发起了。

说了这么多,react-native-webview是支持相机的。

大家在用react-native 相机的时候注意:

1、先看配置清单是否添加权限

2、看下webview是否支持相机,(在系统设置-》应用-》权限中,如果相机权限已经启用,还是调不起相机的话,就要考虑webview是不是不支持)

3、如果安装APP后,相机权限是禁用的,要调用我上面写的方法,主动发起权限申请,webview可以放一个全局的地方申请。

4、input type=file,只有accept="image/*"的时候才有用,并且我测试过:capture={'camera' as any}不是必须的,新版规范中capture也没有camera选项了,只有

capture?: boolean | 'user' | 'environment' | undefined; 这几个值了,没有camera这个了。

就到这,希望大家可以开发顺利。