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
相关产品推荐
相关产品推荐

