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

Kotlin中Retrofit下载ResponseBody后:自动打开、通知及返回处理问题

文件下载与打开功能问题修复方案

问题1:无对应打开应用时无任何反应

直接启动ACTION_VIEW Intent如果没有匹配的Activity会静默失败,必须先做兼容性校验:

  • 调用packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)判断是否有可处理的Activity
  • 若返回null,弹出提示告知用户无可用应用

问题2:无下载完成通知

系统浏览器/邮件的下载通知是通过系统DownloadManager或自定义通知实现的,手动写文件不会自动触发,可通过以下方式实现:

  1. 创建通知渠道(Android 8.0+强制要求)
  2. 构建下载完成通知,点击通知可打开文件
  3. 通知需关联文件的Content URI,确保权限正确

问题3:打开新视图后无法处理返回操作

当前用requireContext().startActivity(intent)启动Activity可能导致返回栈异常,改用requireActivity()启动,确保Activity在当前应用的返回栈中;若需处理返回结果,可使用registerForActivityResult替代直接启动。


修正后的完整代码

override fun onSuccess(result: Response<ResponseBody>) {
    try {
        // 用File构造函数拼接路径,避免字符串拼接错误
        val fileName = "${idEmail}_${intitule.replace("/", "_")}$extensionFichier"
        file = File(requireContext().filesDir, fileName)
        file.writeBytes(result.body()!!.bytes())

        // 修正MIME类型错误,补充标准格式
        val type = when(extensionFichier.lowercase()) {
            ".png", ".jpg", ".jpeg", ".gif" -> "image/*"
            ".pdf" -> "application/pdf"
            ".xls" -> "application/vnd.ms-excel"
            ".xlsx" -> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            ".doc" -> "application/msword"
            ".docx" -> "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
            ".ppt" -> "application/vnd.ms-powerpoint"
            ".pptx" -> "application/vnd.openxmlformats-officedocument.presentationml.presentation"
            ".zip" -> "application/zip"
            ".rar" -> "application/x-rar-compressed"
            ".mp3", ".m4a", ".wav" -> "audio/*"
            ".3gp", ".mpg", ".mpeg", ".mpe", ".mp4", ".avi" -> "video/*"
            ".txt" -> "text/plain"
            else -> "*/*"
        }

        // 构建打开文件的Intent
        val uri = FileProvider.getUriForFile(
            requireContext(),
            BuildConfig.APPLICATION_ID + ".provider",
            file
        )
        val intent = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(uri, type)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        }

        // 校验是否有可用应用处理Intent
        val packageManager = requireContext().packageManager
        if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
            requireActivity().startActivity(intent) // 用Activity启动保证返回栈正常
        } else {
            Toast.makeText(requireContext(), "无可用应用打开此文件", Toast.LENGTH_SHORT).show()
        }

        // 发送下载完成通知
        sendDownloadCompleteNotification(fileName, uri)

    } catch (e: Exception) {
        e.printStackTrace()
        Toast.makeText(requireContext(), "文件处理失败:${e.message}", Toast.LENGTH_SHORT).show()
    }
}

// 发送下载完成通知的方法
private fun sendDownloadCompleteNotification(fileName: String, fileUri: Uri) {
    val context = requireContext()
    // Android 8.0+ 创建通知渠道
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channelId = "download_channel"
        val channelName = "下载通知"
        val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
            description = "文件下载完成通知"
        }
        val notificationManager = context.getSystemService(NotificationManager::class.java)
        notificationManager.createNotificationChannel(channel)
    }

    // 构建通知点击Intent
    val openIntent = Intent(Intent.ACTION_VIEW).apply {
        setDataAndType(fileUri, context.contentResolver.getType(fileUri))
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    }
    val pendingIntent = PendingIntent.getActivity(
        context,
        0,
        openIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    // 构建通知
    val notification = NotificationCompat.Builder(context, "download_channel")
        .setSmallIcon(R.drawable.ic_download_done) // 替换为你的应用图标资源
        .setContentTitle("下载完成")
        .setContentText(fileName)
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)
        .build()

    // 发送通知
    val notificationManager = context.getSystemService(NotificationManager::class.java)
    notificationManager.notify(1001, notification) // 1001为自定义通知ID
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 01:12:00