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

Android Kotlin中使用BroadcastReceiver获取MMS并传递数据的问题

问题解答

一、关于清单注册的问题

你的MmsReceiverK是ComponentActivity子类,必须在清单文件中注册,否则系统无法启动该Activity,动态注册的广播接收器也不会生效。但用Activity监听MMS广播存在局限:只有Activity处于存活状态时,接收器才能接收广播;一旦Activity被销毁,接收器会自动注销,无法实现后台持续监听。若需要后台监听,更推荐用Service结合动态注册,以下先按你的Activity方案实现。

二、修正后的完整实现代码

你的代码存在广播逻辑位置错误、列名硬编码、MMS ID获取错误、IntentFilter配置顺序错误等问题,以下是修正后的Kotlin代码:

package radwil.sms.routing

import android.annotation.SuppressLint
import android.content.*
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.provider.Telephony
import android.provider.Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION
import android.text.TextUtils
import androidx.activity.ComponentActivity
import androidx.core.app.ActivityCompat
import radwil.sms.utils.log
import java.io.IOException

class MmsReceiverK : ComponentActivity() {
    private lateinit var mmsReceiver: BroadcastReceiver
    // 所需危险权限
    private val REQUIRED_PERMISSIONS = arrayOf(
        android.Manifest.permission.RECEIVE_MMS,
        android.Manifest.permission.READ_SMS,
        android.Manifest.permission.RECEIVE_SMS
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Android 6.0+必须动态申请危险权限
        requestPermissions()

        // 初始化广播接收器,将MMS处理逻辑放在onReceive内
        mmsReceiver = object : BroadcastReceiver() {
            @SuppressLint("Range")
            override fun onReceive(context: Context, intent: Intent) {
                log("+++++inside receiver")
                if (TextUtils.equals(intent.action, WAP_PUSH_RECEIVED_ACTION)) {
                    handleMmsReceived(context)
                }
            }
        }

        // 先配置IntentFilter参数,再注册接收器
        val filterMMS = IntentFilter.create(
            "android.provider.Telephony.WAP_PUSH_RECEIVED",
            "application/vnd.wap.mms-message"
        )
        filterMMS.priority = IntentFilter.SYSTEM_HIGH_PRIORITY
        filterMMS.addAction(WAP_PUSH_RECEIVED_ACTION)
        registerReceiver(mmsReceiver, filterMMS)
    }

    private fun requestPermissions() {
        ActivityCompat.requestPermissions(
            this,
            REQUIRED_PERMISSIONS,
            1001
        )
    }

    @SuppressLint("Range")
    private fun handleMmsReceived(context: Context) {
        val contentResolver = context.contentResolver
        // 使用系统常量作为列名,避免硬编码导致的索引找不到问题
        val projection = arrayOf(
            Telephony.Mms._ID,
            Telephony.Mms.CONTENT_TYPE,
            Telephony.Mms.Addr.ADDRESS
        )
        // 直接查询MMS表,按时间倒序取最新一条
        val uri = Telephony.Mms.CONTENT_URI
        contentResolver.query(uri, projection, null, null, "${Telephony.Mms.DATE} DESC")?.use { cursor ->
            if (cursor.moveToFirst()) {
                val contentType = cursor.getString(cursor.getColumnIndex(Telephony.Mms.CONTENT_TYPE))
                if ("application/vnd.wap.multipart.related" == contentType) {
                    // 获取MMS的实际ID(不是列索引)
                    val mmsId = cursor.getLong(cursor.getColumnIndex(Telephony.Mms._ID))
                    val mmsAddress = cursor.getString(cursor.getColumnIndex(Telephony.Mms.Addr.ADDRESS))
                    val mmsText = getMmsText(context, mmsId)

                    log("received MMS from: $mmsAddress with message: $mmsText")
                    // 将MMS数据传递给其他函数
                    passMmsDataToOtherFunction(mmsAddress, mmsText)
                }
            }
        }
    }

    // 修正后的MMS文本提取逻辑
    private fun getMmsText(context: Context, mmsId: Long): String {
        val sb = StringBuilder()
        val partUri = Uri.parse("content://mms/part")
        val projection = arrayOf(Telephony.Mms.Part.TEXT)
        val selection = "${Telephony.Mms.Part.MSG_ID} = ? AND ${Telephony.Mms.Part.CONTENT_TYPE} = 'text/plain'"
        val selectionArgs = arrayOf(mmsId.toString())

        context.contentResolver.query(partUri, projection, selection, selectionArgs, null)?.use { cursor ->
            while (cursor.moveToNext()) {
                val text = cursor.getString(cursor.getColumnIndex(Telephony.Mms.Part.TEXT))
                text?.let { sb.append(it) }
            }
        }
        return sb.toString()
    }

    // 示例:将MMS数据传递给其他业务函数
    private fun passMmsDataToOtherFunction(address: String?, content: String) {
        // 这里实现你的业务逻辑,比如存储、上传等
        log("Passing MMS data: address=$address, content=$content")
    }

    override fun onDestroy() {
        super.onDestroy()
        // 注销接收器,避免内存泄漏
        unregisterReceiver(mmsReceiver)
    }
}

三、关键问题修复说明

  1. 广播逻辑位置错误:原代码将MMS处理逻辑放在onCreate中,只会在Activity启动时执行一次;修正后将逻辑移到BroadcastReceiver的onReceive方法,确保收到广播时触发处理。
  2. 列索引为空问题:原代码使用硬编码列名(如"ct_t"),改用Telephony.Mms系统常量,避免因系统版本/厂商定制导致列名不匹配。
  3. MMS ID获取错误:原代码误将列索引值当作MMS ID,修正后用getLong()获取列的实际数值。
  4. IntentFilter配置顺序:先完成filter的所有参数配置(action、priority),再调用registerReceiver,确保配置生效。
  5. 权限处理:Android 6.0+需动态申请RECEIVE_MMS、READ_SMS等危险权限,否则无法读取MMS数据。

四、清单文件配置

必须在AndroidManifest.xml中注册Activity并声明权限:

<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<!-- Android 13+ 部分机型需添加通知权限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application ...>
    <activity
        android:name=".routing.MmsReceiverK"
        android:exported="true">
        <!-- 可选:如需从桌面启动,添加以下intent-filter -->
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

内容的提问来源于stack exchange,提问作者Will R.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 06:54:25