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

Android WebView多文件选择与相机拍照导入崩溃问题求助

问题分析与修复方案

你的代码在相机拍摄后崩溃,核心问题及对应修复如下:

1. 权限请求的异步逻辑错误

直接启动相机的分支中,调用ActivityCompat.requestPermissions后立刻检查权限并启动相机,但权限请求是异步操作——用户尚未完成授权时,权限检查会返回未授权状态,直接启动相机将触发权限异常崩溃。

修复:
移除runCamera方法中直接启动相机分支里的"请求权限后立即启动相机"代码,改为在onRequestPermissionsResult回调中处理权限授权结果,仅当权限通过后再启动相机。

2. 相机文件存储路径的权限问题

你使用getFilesDir()(应用内部私有目录)存储拍摄图片,相机应用无权限写入该目录,即便通过FileProvider共享,也会因写入权限不足导致崩溃。

修复:
改用应用专属外部存储目录(getExternalFilesDir(Environment.DIRECTORY_PICTURES)),该目录在Android 10及以上无需额外权限,相机应用可正常写入。

3. 相机返回结果的解析逻辑错误

WebChromeClient.FileChooserParams.parseResult用于解析系统文件选择器返回的数据,手动构造的Intent无法被正确解析,会导致传递给回调的Uri数组异常,引发崩溃。

修复:
相机拍摄完成后,直接构造Uri[]数组传递给filePathCallbackLollipop,无需调用parseResult。

4. 重复文件名导致的文件冲突

每次拍摄使用固定文件名picture.png,若文件已存在,会触发文件写入时的IO异常。

修复:
用时间戳生成唯一文件名,避免文件覆盖。


修改后的关键代码片段

1. 修正runCamera方法

private void runCamera(boolean _isCapture) {
    Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // 生成唯一文件名
    String fileName = System.currentTimeMillis() + ".png";
    // 使用应用专属外部存储目录
    File path = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    if (path == null) {
        Toast.makeText(this, "存储目录不可用", Toast.LENGTH_SHORT).show();
        return;
    }
    File file = new File(path, fileName);
    // 获取文件URI
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        String strpa = getApplicationContext().getPackageName();
        cameraImageUri = FileProvider.getUriForFile(getApplicationContext(), strpa + ".provider", file);
    } else {
        cameraImageUri = Uri.fromFile(file);
    }
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri);

    Intent fileIntent = new Intent(Intent.ACTION_GET_CONTENT);
    fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
    fileIntent.setType("image/*");  // 仅显示图片类型

    if (!_isCapture) { // 弹出选择器,包含相机和相册
        Intent pickIntent = new Intent(Intent.ACTION_PICK);
        pickIntent.setType(MediaStore.Images.Media.CONTENT_TYPE);
        pickIntent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        pickIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); // 支持多选

        String pickTitle = "选择获取图片的方式";
        Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);

        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{ fileIntent });
            startActivityForResult(chooserIntent, FILECHOOSER_LOLLIPOP_REQ_CODE);
            return;
        } else {
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{ intentCamera, fileIntent });
            startActivityForResult(chooserIntent, FILECHOOSER_LOLLIPOP_REQ_CODE);
        }
    } else {// 直接启动相机
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
            // 仅当权限未授权时请求权限
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 3);
            return;
        }
        // 权限已授权,启动相机
        startActivityForResult(intentCamera, FILECHOOSER_LOLLIPOP_REQ_CODE);
    }
}

2. 添加权限回调处理

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 3) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限授权成功,重新启动相机
            runCamera(true);
        } else {
            Toast.makeText(this, "相机权限被拒绝,无法拍摄图片", Toast.LENGTH_SHORT).show();
            // 回调置空,避免后续无法触发选择
            if (filePathCallbackLollipop != null) {
                filePathCallbackLollipop.onReceiveValue(null);
                filePathCallbackLollipop = null;
            }
        }
    }
}

3. 修正onActivityResult中的相机结果处理

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d("requestCode: ", String.valueOf(requestCode));
    Log.d("resultCode : ", String.valueOf(resultCode));

    switch (requestCode) {
        case FILECHOOSER_NORMAL_REQ_CODE:
            if (resultCode == RESULT_OK) {
                if (filePathCallbackNormal == null) return;
                Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
                filePathCallbackNormal.onReceiveValue(result);
                filePathCallbackNormal = null;
            }
            break;
        case FILECHOOSER_LOLLIPOP_REQ_CODE:
            if (resultCode == RESULT_OK) {
                if (filePathCallbackLollipop == null) return;
                Uri[] results;
                if (data != null && data.getData() != null) {
                    // 处理相册选择(含多选)
                    if (data.getClipData() != null) {
                        int count = data.getClipData().getItemCount();
                        results = new Uri[count];
                        for (int i = 0; i < count; i++) {
                            results[i] = data.getClipData().getItemAt(i).getUri();
                        }
                    } else {
                        results = new Uri[]{data.getData()};
                    }
                } else {
                    // 处理相机拍摄
                    results = new Uri[]{cameraImageUri};
                }
                filePathCallbackLollipop.onReceiveValue(results);
                filePathCallbackLollipop = null;
            } else {
                if (filePathCallbackLollipop != null) {
                    filePathCallbackLollipop.onReceiveValue(null);
                    filePathCallbackLollipop = null;
                }
                if (filePathCallbackNormal != null) {
                    filePathCallbackNormal.onReceiveValue(null);
                    filePathCallbackNormal = null;
                }
            }
            break;
        default:
            break;
    }
    super.onActivityResult(requestCode, resultCode, data);
}

额外注意事项

  • 确保AndroidManifest.xml中已正确配置FileProvider,Android 9及以下需添加WRITE_EXTERNAL_STORAGE权限。
  • 测试前清理应用缓存,避免旧文件路径引发异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 04:52:00