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

Android WebView调用相机失败求助:相册正常相机无法启动

我来帮你搞定这个WebView调用相机失败的问题!结合你说的相册正常、已经加了权限的情况,大概率是WebView的配置或者文件上传的回调处理没做到位,给你几个关键的排查方向和解决方案:

1. 先检查WebView的核心配置是否齐全

WebView要支持H5的文件上传(包括相机调用),必须开启几个关键设置,别漏了:

  • 开启JavaScript:webSettings.setJavaScriptEnabled(true); (H5调相机依赖JS,这个你大概率已经加了,但再确认下)
  • 开启文件访问权限:webSettings.setAllowFileAccess(true);
  • 针对Android 5.0及以上,还要开启跨域文件访问支持:webSettings.setAllowFileAccessFromFileURLs(true); 和 webSettings.setAllowUniversalAccessFromFileURLs(true);
  • Android 11+的话,建议加上webSettings.setMediaPlaybackRequiresUserGesture(false);,避免手势限制影响相机触发
2. 必须实现WebChromeClient的文件选择回调

这是最容易踩坑的点!WebView默认不会处理H5的文件选择请求,你得重写WebChromeClient的回调方法,手动处理相机和相册的选择逻辑。给你一个完整的示例代码:

// 先定义全局变量保存回调和临时图片路径
private ValueCallback<Uri[]> mFilePathCallback;
private String mCameraPhotoPath;
private static final int REQUEST_SELECT_FILE = 100;

// 设置WebChromeClient
webView.setWebChromeClient(new WebChromeClient() {
    // Android 5.0+ 版本的文件选择回调
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        // 保存回调,后续在onActivityResult里返回结果
        mFilePathCallback = filePathCallback;
        
        // 构建相机拍摄的Intent
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            // 创建临时文件存储拍摄的图片
            File photoFile = null;
            try {
                photoFile = createImageFile();
                mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            if (photoFile != null) {
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
            } else {
                takePictureIntent = null;
            }
        }

        // 构建相册选择的Intent
        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
        contentSelectionIntent.setType("image/*");

        // 整合相机和相册选项,弹出选择器
        Intent[] intentArray = takePictureIntent != null ? new Intent[]{takePictureIntent} : new Intent[0];
        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, "选择图片来源");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
        startActivityForResult(chooserIntent, REQUEST_SELECT_FILE);
        return true;
    }

    // 兼容Android 4.x的旧版本回调(可选)
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        ValueCallback<Uri[]> callback = new ValueCallback<Uri[]>() {
            @Override
            public void onReceiveValue(Uri[] value) {
                if (uploadMsg != null) {
                    uploadMsg.onReceiveValue(value != null && value.length > 0 ? value[0] : null);
                }
            }
        };
        onShowFileChooser(webView, callback, null);
    }
});

// 辅助方法:创建临时图片文件
private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    return File.createTempFile(imageFileName, ".jpg", storageDir);
}

然后还要在Activity里处理返回结果,把图片Uri传给WebView:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_SELECT_FILE) {
        if (mFilePathCallback == null) return;
        
        Uri[] results = null;
        if (resultCode == RESULT_OK) {
            if (data == null) {
                // 相机拍摄的情况,用临时文件路径生成Uri
                if (mCameraPhotoPath != null) {
                    results = new Uri[]{Uri.parse(mCameraPhotoPath)};
                }
            } else {
                // 相册选择的情况,直接取返回的Uri
                String dataString = data.getDataString();
                if (dataString != null) {
                    results = new Uri[]{Uri.parse(dataString)};
                }
            }
        }
        // 把结果返回给WebView
        mFilePathCallback.onReceiveValue(results);
        mFilePathCallback = null;
    }
}
3. 确认权限的动态申请(重中之重!)

你说已经加了相机权限,但Android 6.0+必须动态申请权限,只在Manifest里声明是没用的。需要申请的权限包括:

  • android.permission.CAMERA
  • Android 10及以下:android.permission.WRITE_EXTERNAL_STORAGE
  • Android 13及以上:android.permission.READ_MEDIA_IMAGES(替代READ_EXTERNAL_STORAGE)

动态申请的代码示例:

private static final int REQUEST_PERMISSIONS = 200;

private void requestRequiredPermissions() {
    List<String> needPermissions = new ArrayList<>();
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        needPermissions.add(Manifest.permission.CAMERA);
    }
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            needPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
    } else {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
            needPermissions.add(Manifest.permission.READ_MEDIA_IMAGES);
        }
    }
    
    if (!needPermissions.isEmpty()) {
        ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[0]), REQUEST_PERMISSIONS);
    }
}

// 处理权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_PERMISSIONS) {
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (allGranted) {
            // 权限都获取了,重新加载WebView或者触发相机选择
            webView.reload();
        } else {
            Toast.makeText(this, "需要相机和存储权限才能使用相机功能", Toast.LENGTH_SHORT).show();
        }
    }
}

记得在启动WebView之前先调用requestRequiredPermissions()申请权限!

4. Android版本适配细节
  • Android 10+:如果用上面的临时文件方式保存相机图片,需要在Manifest的application标签里添加android:requestLegacyExternalStorage="true",否则会因为Scoped Storage限制无法访问文件。
  • Android 11+:确保你使用的是AndroidX的WebView组件,避免旧版本的兼容性问题。
  • Android 13+:别忘添加READ_MEDIA_IMAGES权限,否则无法读取相册图片。
5. 最后确认H5的input标签是否正确

虽然你说相册正常,但还是确认下H5的文件选择标签是否支持相机,比如:

<!-- 允许用户选择相机或相册 -->
<input type="file" accept="image/*">
<!-- 直接调用相机 -->
<input type="file" accept="image/*" capture="camera">

按照上面的步骤逐一排查,尤其是WebChromeClient的回调和动态权限申请,这两个是90%的概率会出问题的地方。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:09:39