OnActivityResult传递结果失败:空指针异常技术求助
问题原因分析
从错误栈能直接定位核心问题:在new File(filePath)这一行触发了空指针异常,根源是filePath的值为null。
出现这个问题的具体原因有两个:
- API版本限制:
MediaStore.Images.Media.DATA字段在Android 10(API 29)及以上已被废弃,系统不再返回真实的本地文件路径,通过该字段查询得到的结果自然是null。 - Uri类型问题:你选中图片返回的是
content://类型的Uri(比如日志里的content://com.android.providers.media.documents/document/image:28362),这类Uri指向的是系统媒体库的文档条目,而非直接的文件路径,用MediaStore.Images.Media.DATA根本查询不到有效路径。
解决方案
不要再尝试通过MediaStore获取文件路径,而是直接通过ContentResolver打开Uri对应的输入流,将图片内容复制到应用私有目录下的临时文件,再用这个临时文件完成上传操作。
修改后的onActivityResult代码示例:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK && data != null) { Uri selectedImage = data.getData(); if (selectedImage == null) return; File tempFile = null; try { // 获取图片MIME类型,用于设置临时文件后缀 String mimeType = getContentResolver().getType(selectedImage); String fileSuffix = mimeType != null ? mimeType.split("/")[1] : "jpg"; // 在应用私有缓存目录创建临时文件,无需额外权限 tempFile = File.createTempFile("upload_temp", "." + fileSuffix, getCacheDir()); // 将Uri对应的图片内容复制到临时文件 InputStream inputStream = getContentResolver().openInputStream(selectedImage); FileOutputStream outputStream = new FileOutputStream(tempFile); byte[] buffer = new byte[1024]; int readLength; while ((readLength = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, readLength); } // 关闭流资源 inputStream.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); return; } // 基于临时文件创建上传所需的RequestBody和Multipart部件 RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), tempFile); // 获取图片原文件名,提升上传的友好性 String originalFileName = getOriginalFileName(selectedImage); MultipartBody.Part body = MultipartBody.Part.createFormData( "banner", originalFileName != null ? originalFileName : tempFile.getName(), reqFile ); RequestBody name = RequestBody.create(MediaType.parse("text/plain"), "banner"); // 发起上传请求 retrofit2.Call<okhttp3.ResponseBody> req = service.postImage(body, name); req.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { // 上传完成后清理临时文件,避免占用存储空间 if (tempFile != null && tempFile.exists()) { tempFile.delete(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e("FFF", t.getMessage()); t.printStackTrace(); // 上传失败也清理临时文件 if (tempFile != null && tempFile.exists()) { tempFile.delete(); } } }); } } // 辅助方法:通过Uri获取图片原文件名 private String getOriginalFileName(Uri uri) { String fileName = null; if ("content".equals(uri.getScheme())) { Cursor cursor = getContentResolver().query(uri, null, null, null, null); try { if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); } } finally { if (cursor != null) { cursor.close(); } } } // 如果content类型查询不到,就用Uri的最后一段作为文件名 if (fileName == null) { fileName = uri.getLastPathSegment(); } return fileName; }
额外注意事项:
- 若你的应用需要适配Android 10以下版本,需要在Manifest中声明
android.permission.READ_EXTERNAL_STORAGE权限,并在运行时申请;Android 10及以上使用Scoped Storage,无需额外权限即可访问媒体库文件。 - 临时文件创建在应用私有缓存目录,不会暴露给其他应用,安全性更高。
内容的提问来源于stack exchange,提问作者Abhishek S
相关产品推荐
相关产品推荐

