You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android WebView相册上传正常但相机拍照上传失效问题咨询(含Android11适配)

问题描述

搜索本站所有相关问题帖子,仍无法定位Android 11设备上WebView调用相机拍照上传图片失效的问题,相册上传功能完全正常。在onActivityResult回调中可以获取到缓存目录下的图片路径,但网页端无法访问该文件,或是缓存目录下的图片文件为空。直接使用设备自带的谷歌Chrome浏览器可以正常拍照并上传图片,可排除网页侧问题。当前代码在Android 10及以下版本运行正常,Android 11版本拍照完成后图片无法填充到WebView的文件上传输入框中,推测是FileProvider适配或配置存在问题。

已配置权限

<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<!-- for new versions api 21+-->
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

AndroidManifest.xml application节点配置

<application
    android:name="***"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:requestLegacyExternalStorage="true"
    android:hardwareAccelerated="true"
    android:theme="@style/Theme.Android">

    <!-- added fileprovider support -->
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

file_paths.xml配置

<paths>
  <cache-path
    name="mediaimages"
    path="./">
  </cache-path>
</paths>

核心业务代码

private var mUploadCallbackAboveL: ValueCallback<Array<Uri>>? = null
private var mUploadCallbackBelow: ValueCallback<Uri?>? = null
private var imageUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {   
  super.onCreate(savedInstanceState)
  // webview definition 
  myWebView.webChromeClient = initChromeWebViewClient()
  myWebView.webViewClient = initWebViewClient()
  myWebView.settings.javaScriptEnabled = true
  myWebView.settings.domStorageEnabled = true
  myWebView.settings.allowFileAccess = true
  myWebView.settings.allowContentAccess = true
  myWebView.settings.supportZoom()
  myWebView.visibility = View.INVISIBLE
  myWebView.settings.userAgentString = "android"
  myWebView.setLayerType(View.LAYER_TYPE_HARDWARE, null)

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      myWebView.settings.mixedContentMode = 0
  }
}

private fun initChromeWebViewClient() : WebChromeClient {
    webChromeViewClient = object : WebChromeClient() {
        /**
         * API >= 21 (Android 5.0.1) calls back this method
         */
        override fun onShowFileChooser(
            webView: WebView?,
            valueCallback: ValueCallback<Array<Uri>>,
            fileChooserParams: FileChooserParams?
        ): Boolean {
            mUploadCallbackAboveL = valueCallback
            takePhoto()
            return true
        }
    }
    return webChromeViewClient
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE) {
        val file: File = File(imageUri!!.toString())
        val file_size = (file.length() / 1024).toString().toInt()
        if (mUploadCallbackBelow != null) {
            chooseBelow(resultCode, data);
        } else if (mUploadCallbackAboveL != null) {
            chooseAbove(resultCode, data);
        } else {
            Toast.makeText (this, "an error occurred.", Toast.LENGTH_SHORT).show();
        }
    }
}

拍照Intent创建逻辑

private fun takePhoto() {
    var photoFile : File? = null
    val authorities : String = applicationContext.packageName + ".provider"
    try {
        photoFile = createImageFile()
        imageUri = FileProvider.getUriForFile(this, authorities, photoFile)
    } catch(e: IOException) {
        e.printStackTrace()
    }

    val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
    val Photo = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    val chooserIntent = Intent.createChooser(Photo, "Image Chooser")
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf<Parcelable>(captureIntent))
    startActivityForResult(chooserIntent, REQUEST_CODE)
}

@Throws(IOException::class)
  private fun createImageFile(): File {
    val imageFileName = "JPEG_" + SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    return File(applicationContext.cacheDir.toString() + File.separator + imageFileName + ".jpg")
}

结果处理逻辑

private fun chooseAbove(resultCode : Int, data : Intent?) {
    if (RESULT_OK == resultCode) {
        updatePhotos()

        if (data != null) {
            val results : Array<Uri>
            val uriData : Uri? = data.data
            if (uriData != null) {
                results = arrayOf(uriData)
                mUploadCallbackAboveL!!.onReceiveValue(results)
            } else {
                mUploadCallbackAboveL!!.onReceiveValue(null)
            }
        } else {
            if (imageUri != null) {
                mUploadCallbackAboveL!!.onReceiveValue(arrayOf(imageUri!!))
            }
        }
    } else {
        mUploadCallbackAboveL!!.onReceiveValue(null)
    }
    mUploadCallbackAboveL = null
}

解决方案

按以下步骤依次修改即可修复Android 11适配问题:

  1. 清理并更新权限配置
    删除无效的WRITE_INTERNAL_STORAGE、READ_INTERNAL_STORAGE、重复的CAMERA权限,新增Android 11必要配置:
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<!-- Android 11+ 媒体访问权限 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
  1. 更新FileProvider路径配置
    修改res/xml/file_paths.xml,新增外部缓存路径适配:
<paths>
  <cache-path
    name="internal_media"
    path="." />
  <external-cache-path
    name="external_media"
    path="." />
</paths>
  1. 补充相机Intent的Uri授权
    Android 11对跨应用Uri访问限制更严格,需要给相机应用显式授予读写权限,修改takePhoto方法:
private fun takePhoto() {
    var photoFile : File? = null
    val authorities : String = applicationContext.packageName + ".provider"
    try {
        // 优先使用外部缓存,避免部分机型相机无法写入内部缓存
        val saveDir = if (externalCacheDir != null) externalCacheDir else cacheDir
        val imageFileName = "JPEG_" + SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
        photoFile = File(saveDir, "$imageFileName.jpg")
        imageUri = FileProvider.getUriForFile(this, authorities, photoFile)
    } catch(e: IOException) {
        e.printStackTrace()
    }

    val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
    // 给所有可处理拍照请求的应用授予Uri读写权限
    val resolveInfos = packageManager.queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY)
    for (info in resolveInfos) {
        grantUriPermission(info.activityInfo.packageName, imageUri, 
            Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    }
    
    val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    val chooserIntent = Intent.createChooser(pickIntent, "选择图片")
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf<Parcelable>(captureIntent))
    startActivityForResult(chooserIntent, REQUEST_CODE)
}
  1. 修复onActivityResult错误逻辑
    删除错误的File转换代码,imageUri是content://协议的Uri,不能直接转为File路径:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE) {
        if (mUploadCallbackBelow != null) {
            chooseBelow(resultCode, data)
        } else if (mUploadCallbackAboveL != null) {
            chooseAbove(resultCode, data)
        } else {
            Toast.makeText (this, "发生错误", Toast.LENGTH_SHORT).show()
        }
    }
}
  1. 补充WebView权限配置
    在onCreate的WebView初始化部分新增跨域访问配置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    myWebView.settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
    myWebView.settings.allowFileAccessFromFileURLs = true
    myWebView.settings.allowUniversalAccessFromFileURLs = true
}

内容的提问来源于stack exchange,提问作者Vario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 07:18:04