React Native中UssdModule无法唤起窗口,需将USSD结果返回前端的求助
React Native USSD模块问题解决方案
问题根源
你的代码存在两个核心问题:
- 使用
Intent.ACTION_CALL会直接发起通话,而非唤起USSD交互窗口,部分机型会拦截此类操作 - 原实现无法捕获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
相关产品推荐
相关产品推荐

