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

React Native中UssdModule无法唤起窗口,需将USSD结果返回前端的求助

React Native USSD模块问题解决方案

问题根源

你的代码存在两个核心问题:

  1. 使用Intent.ACTION_CALL会直接发起通话,而非唤起USSD交互窗口,部分机型会拦截此类操作
  2. 原实现无法捕获USSD操作结果,因为startActivity没有回调机制

修复方案

1. 添加必要权限

在AndroidManifest.xml中声明权限(Android 6+需要动态申请危险权限):

<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

2. 重写原生模块(支持结果返回)

使用Android 8.0+提供的TelephonyManager.UssdResponseCallback发送USSD请求并获取结果,同时兼容低版本系统:

class UssdModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), LifecycleEventListener {

    private var telephonyManager: TelephonyManager? = null
    private var ussdCallback: UssdResponseCallback? = null
    private var promise: Promise? = null

    override fun getName(): String {
        return "UssdModule"
    }

    init {
        reactContext.addLifecycleEventListener(this)
        telephonyManager = reactContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
    }

    @ReactMethod
    fun sendUssd(ussdCode: String, promise: Promise) {
        this.promise = promise

        // 检查CALL_PHONE权限
        if (ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            promise.reject("PERMISSION_DENIED", "缺少CALL_PHONE权限")
            return
        }

        // 正确编码USSD码中的特殊字符
        val encodedUssd = ussdCode.replace("#", Uri.encode("#"))
        val ussdUri = "tel:$encodedUssd"

        // Android 8.0+ 直接获取USSD结果
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ussdCallback = object : UssdResponseCallback() {
                override fun onReceiveUssdResponse(telephony: TelephonyManager, response: UssdResponse) {
                    promise?.resolve(response.message)
                    cleanUp()
                }

                override fun onReceiveUssdResponseFailed(telephony: TelephonyManager, failureCode: Int) {
                    val errorMsg = when (failureCode) {
                        TELEPHONY_ERROR_CALL_FAILED -> "通话连接失败"
                        TELEPHONY_ERROR_NO_SERVICE -> "当前无网络服务"
                        TELEPHONY_ERROR_RADIO_OFF -> "手机无线电已关闭"
                        else -> "USSD请求失败,错误码:$failureCode"
                    }
                    promise?.reject("USSD_FAILED", errorMsg)
                    cleanUp()
                }
            }
            telephonyManager?.sendUssdRequest(ussdUri, ussdCallback, Handler(Looper.getMainLooper()))
        } else {
            // 低版本系统只能唤起拨号界面,无法自动获取结果
            val intent = Intent(Intent.ACTION_DIAL, Uri.parse(ussdUri))
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            reactApplicationContext.startActivity(intent)
            promise?.resolve("已打开拨号界面,请手动触发USSD操作")
        }
    }

    private fun cleanUp() {
        promise = null
        ussdCallback = null
    }

    override fun onHostResume() {}
    override fun onHostPause() {}
    override fun onHostDestroy() = cleanUp()
}

3. 注册模块

创建Package类并注册USSD模块:

class UssdPackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(UssdModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

4. 前端调用示例

import { NativeModules, PermissionsAndroid } from 'react-native';
const { UssdModule } = NativeModules;

// 申请动态权限
async function requestCallPermission() {
  try {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.CALL_PHONE,
      {
        title: "USSD权限申请",
        message: "需要获取拨打电话权限以发送USSD请求",
        buttonNeutral: "稍后再说",
        buttonNegative: "取消",
        buttonPositive: "允许"
      }
    );
    return granted === PermissionsAndroid.RESULTS.GRANTED;
  } catch (err) {
    console.warn(err);
    return false;
  }
}

// 发送USSD请求
async function sendUssdCode(code) {
  const hasPermission = await requestCallPermission();
  if (!hasPermission) return;
  
  try {
    const result = await UssdModule.sendUssd(code);
    console.log("USSD返回结果:", result);
    // 处理结果逻辑
  } catch (error) {
    console.error("USSD请求失败:", error);
    // 处理错误逻辑
  }
}

// 调用示例
sendUssdCode('*123#');

关键说明

  • Android 8.0以下系统无法自动获取USSD结果,只能唤起拨号界面让用户手动操作
  • 部分厂商会限制自动USSD请求,需在不同机型测试兼容性
  • 必须确保用户授予CALL_PHONE权限,否则请求会失败

内容的提问来源于stack exchange,提问作者kenanda abdelouahad rabah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:33:23