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

基于Expo的React Native实现来电及挂断时弹出弹窗

实现Expo Android应用来电状态监听及弹窗功能

方案一:Bare Workflow(已 eject 的 Expo 项目)

这种方式可以直接编写原生代码实现广播接收器,步骤如下:

  1. 配置权限与广播接收器
    在android/app/src/main/AndroidManifest.xml中添加权限和广播接收器声明:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 如需获取通话详情可添加此权限 -->
<uses-permission android:name="android.permission.READ_CALL_LOG" />

<receiver android:name=".PhoneStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>
  1. 编写广播接收器类
    在android/app/src/main/java/com/[你的应用包名]/下创建PhoneStateReceiver.java,负责监听来电状态并通知JS层:
package com.[你的应用包名];

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class PhoneStateReceiver extends BroadcastReceiver {
    private static ReactApplicationContext reactContext;
    private String lastState = TelephonyManager.EXTRA_STATE_IDLE;

    public static void setReactContext(ReactApplicationContext context) {
        reactContext = context;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

        if (state != null && !state.equals(lastState)) {
            lastState = state;
            if (reactContext != null) {
                reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                        .emit("phoneStateChanged", state + "," + incomingNumber);
            }
        }
    }
}
  1. 初始化接收器上下文
    在MainApplication.java的onCreate方法中添加上下文初始化:
@Override
public void onCreate() {
    super.onCreate();
    PhoneStateReceiver.setReactContext(getReactNativeHost().getReactApplicationContext());
}
  1. JS层监听事件并弹窗
    使用React Native的NativeEventEmitter监听原生事件,触发弹窗:
import { Alert, NativeEventEmitter, NativeModules, PermissionsAndroid } from 'react-native';

// 动态申请电话权限(Android 6+ 必需)
async function requestPhonePermission() {
  const granted = await PermissionsAndroid.request(
    PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE,
    {
      title: '电话权限申请',
      message: '应用需要访问电话状态来监听来电',
      buttonPositive: '允许',
      buttonNegative: '取消',
    },
  );
  return granted === PermissionsAndroid.RESULTS.GRANTED;
}

// 组件内监听事件
componentDidMount() {
  requestPhonePermission().then(granted => {
    if (granted) {
      const eventEmitter = new NativeEventEmitter(NativeModules.DeviceEventManagerModule);
      this.subscription = eventEmitter.addListener('phoneStateChanged', data => {
        const [state, number] = data.split(',');
        if (state === 'RINGING') {
          Alert.alert('来电提醒', `来电号码:${number || '未知'}`);
        } else if (state === 'IDLE') {
          Alert.alert('通话结束', '已挂断电话');
        }
      });
    }
  });
}

// 组件卸载时取消订阅
componentWillUnmount() {
  this.subscription?.remove();
}

方案二:Managed Workflow(未 eject 的 Expo 项目)

通过自定义Config Plugin集成原生代码,无需手动eject:

  1. 创建Config Plugin文件
    在项目根目录创建withPhoneStateReceiver.js,用来自动修改Android配置和添加原生代码:
const { withAndroidManifest, withDangerousMod } = require('@expo/config-plugins');
const fs = require('fs');
const path = require('path');

module.exports = function withPhoneStateReceiver(config) {
  // 修改AndroidManifest.xml添加权限和接收器
  config = withAndroidManifest(config, async config => {
    const manifest = config.modResults.manifest;
    // 添加权限
    manifest['uses-permission'].push({
      $: { 'android:name': 'android.permission.READ_PHONE_STATE' }
    });
    // 添加广播接收器
    manifest.application[0].receiver.push({
      $: { 'android:name': '.PhoneStateReceiver' },
      'intent-filter': [{
        action: [{ $: { 'android:name': 'android.intent.action.PHONE_STATE' } }]
      }]
    });
    return config;
  });

  // 添加广播接收器Java文件
  config = withDangerousMod(config, [
    'android',
    async config => {
      const srcPath = path.join(config.modRequest.platformProjectRoot, 'app/src/main/java/com/[你的应用包名]');
      if (!fs.existsSync(srcPath)) fs.mkdirSync(srcPath, { recursive: true });
      
      const receiverCode = `package com.[你的应用包名];

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class PhoneStateReceiver extends BroadcastReceiver {
    private static ReactApplicationContext reactContext;
    private String lastState = TelephonyManager.EXTRA_STATE_IDLE;

    public static void setReactContext(ReactApplicationContext context) {
        reactContext = context;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

        if (state != null && !state.equals(lastState)) {
            lastState = state;
            if (reactContext != null) {
                reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                        .emit("phoneStateChanged", state + "," + incomingNumber);
            }
        }
    }
}`;
      fs.writeFileSync(path.join(srcPath, 'PhoneStateReceiver.java'), receiverCode);
      return config;
    }
  ]);

  // 修改MainApplication.java初始化接收器
  config = withDangerousMod(config, [
    'android',
    async config => {
      const mainAppPath = path.join(config.modRequest.platformProjectRoot, 'app/src/main/java/com/[你的应用包名]/MainApplication.java');
      let content = fs.readFileSync(mainAppPath, 'utf8');
      
      // 添加导入语句
      if (!content.includes('import com.[你的应用包名].PhoneStateReceiver;')) {
        content = content.replace('import com.facebook.react.ReactApplication;', '$&\nimport com.[你的应用包名].PhoneStateReceiver;');
      }
      // 添加初始化代码
      if (!content.includes('PhoneStateReceiver.setReactContext')) {
        content = content.replace('@Override\npublic void onCreate() {', '$&\n    PhoneStateReceiver.setReactContext(getReactNativeHost().getReactApplicationContext());');
      }
      fs.writeFileSync(mainAppPath, content);
      return config;
    }
  ]);

  return config;
};
  1. 配置App.json
    在app.json的expo.plugins中添加自定义插件:
{
  "expo": {
    "plugins": ["./withPhoneStateReceiver.js"]
  }
}
  1. JS层代码
    与Bare Workflow的JS层代码完全一致,包括权限申请和事件监听弹窗逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 17:25:23