使用ActivityResultLauncher从相册选图上传头像选择后闪退如何解决
问题根因
- 你当前写法错误获取返回的图片Uri:
ACTION_PICK返回的选中图片Uri直接存在返回的Intent对象本身,不是存到Extras里的,你调用data.get("data")拿到的是null,后续赋值就会触发空指针闪退。 - 缺少Uri持久化授权:Android系统返回的相册Uri默认是临时权限,应用退到后台/重启后再访问会报错,需要主动申请持久化权限才能长期存在数据库里用。
- 缺少动态权限申请:Android 6.0+需要动态申请存储权限,Android13+需要申请
READ_MEDIA_IMAGES权限,权限缺失也会触发闪退。
完整修改方案
第一步:添加权限声明(AndroidManifest.xml)
<!-- Android 12及以下存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13及以上图片权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
第二步:修改业务代码(更推荐直接用GetContent协议,无需手动构造Intent)
// 初始化图片选择器 ActivityResultLauncher<String> imagePickerLauncher = registerForActivityResult( new ActivityResultContracts.GetContent(), uri -> { if (uri != null) { // 申请Uri持久化权限,方便后续存数据库后长期访问 final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION; getContentResolver().takePersistableUriPermission(uri, takeFlags); // 赋值给头像控件 profilePic.setImageURI(uri); // 这里可以把uri.toString()存到你的用户数据库里 String saveUriStr = uri.toString(); } } ); // 上传按钮点击逻辑 uploadPicture.setOnClickListener(view -> { if (checkStoragePermission()) { imagePickerLauncher.launch("image/*"); } else { requestStoragePermission(); } });
第三步:补充权限检查辅助方法
private boolean checkStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED; } else { return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } } private void requestStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_MEDIA_IMAGES}, 1001); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001); } } // 权限申请回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1001) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { imagePickerLauncher.launch("image/*"); } } }
若要保留原有StartActivityForResult写法,仅修改返回处理逻辑即可
activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK && result.getData() != null) { // 直接从Intent拿Uri,不要读取Extras Uri myUri = result.getData().getData(); if (myUri != null) { // 同样申请持久化权限 final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION; getContentResolver().takePersistableUriPermission(myUri, takeFlags); profilePic.setImageURI(myUri); } } });
额外注意
setImageURI是在UI线程直接加载图片,如果选的原图很大可能会导致卡顿或者ANR,实际生产环境建议用Glide、Coil等图片加载框架加载Uri到头像控件,自动处理采样压缩。- 存数据库的时候存
uri.toString()的字符串就行,后续要使用的时候用Uri.parse(uriStr)转回Uri对象即可。
内容的提问来源于stack exchange,提问作者mendoh
相关产品推荐
相关产品推荐

