Kotlin如何实现单按钮选图后自动上传至Firebase Storage
问题原因
你的代码出现选图后需要二次点击才上传的核心问题是执行顺序错误:
- 图库选图是异步跳转流程,调用
activityResultLauncher.launch(intentToGallery)之后,代码会继续往下执行,不会等待用户选完图再运行后续逻辑 - 你把Firebase Storage上传逻辑直接写在了按钮的长按回调里,第一次触发按钮时,刚拉起图库,
selectedPicture变量还是空值,上传的非空判断直接跳过 - 等用户选完图回到页面,
selectedPicture才会在activityResultLauncher的结果回调里被赋值,这时候上传逻辑早就执行完毕,只有第二次长按按钮的时候,selectedPicture已经存了上一次选中的图片Uri,才会触发上传操作。
修复方案
- 把所有上传相关逻辑从按钮长按回调中抽离,封装成独立的上传方法
- 在选图结果回调中,拿到选中的图片Uri后直接调用上传方法,实现选完即传的连贯流程
- 补充Android版本权限适配,Android 13(API 33)及以上版本读取图库图片需要使用
READ_MEDIA_IMAGES权限,旧的READ_EXTERNAL_STORAGE权限在高版本系统上会失效 - 补充上传失败的回调逻辑,方便排查异常场景
修复后代码示例
1. 按钮长按事件(仅保留权限校验、拉起图库逻辑)
idgir.setOnLongClickListener { // 分版本做权限校验 val requiredPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { Manifest.permission.READ_MEDIA_IMAGES } else { Manifest.permission.READ_EXTERNAL_STORAGE } if (ContextCompat.checkSelfPermission(this, requiredPermission) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, requiredPermission)) { Snackbar.make( binding.root, "需要图库权限才能选择图片", Snackbar.LENGTH_INDEFINITE ).setAction("授予权限") { permissionLauncher.launch(requiredPermission) }.show() } else { permissionLauncher.launch(requiredPermission) } } else { val intentToGallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) activityResultLauncher.launch(intentToGallery) } // 消费长按事件,避免触发其他默认逻辑 return@setOnLongClickListener true }
2. 注册结果启动器(选图成功后直接触发上传)
private fun registerLauncher() { activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { val intentFromResult = result.data if (intentFromResult != null) { val selectedPicture = intentFromResult.data selectedPicture?.let { binding.idgir.setImageURI(it) // 选到图片后直接调用上传方法,无需二次点击 uploadImageToFirebase(it) } } } } permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> if (isGranted) { val intentToGallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) activityResultLauncher.launch(intentToGallery) } else { Toast.makeText(this@MainActivity, "需要授予图库权限才能选择图片", Toast.LENGTH_LONG).show() } } }
3. 抽离独立的Firebase上传方法
private fun uploadImageToFirebase(imageUri: Uri) { val uuid = UUID.randomUUID() val imageName = "$uuid.jpg" val reference = storage.reference val imageReference = reference.child("images").child(imageName) imageReference.putFile(imageUri) .addOnSuccessListener { // 上传文件成功后获取可访问的下载链接 val uploadPictureReference = storage.reference.child("images").child(imageName) uploadPictureReference.downloadUrl.addOnSuccessListener { downloadUri -> // 此处可添加后续业务逻辑,比如将下载链接存入数据库、更新UI等 Toast.makeText(this@MainActivity, "图片上传成功", Toast.LENGTH_SHORT).show() } } .addOnFailureListener { e -> // 处理上传失败场景 Toast.makeText(this@MainActivity, "图片上传失败:${e.message}", Toast.LENGTH_SHORT).show() } }
额外配置说明
- 需要在
AndroidManifest.xml中补充对应权限声明,兼容高低版本系统:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
- 确保Firebase Storage规则配置正确,测试阶段可临时设置为允许认证用户读写,上线前记得调整为符合业务安全要求的规则。
内容的提问来源于stack exchange,提问作者terlan abaszade
相关产品推荐
相关产品推荐

