首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android 監聽未接來電兩種方案:BroadcastReceiver 與 PhoneStateListener 詳解

     編輯:Java 相關知識

在 Android 開發中,監聽電話狀態變化以檢測未接來電是常見需求。本文對比 BroadcastReceiver(廣播接收器) 與 PhoneStateListener(電話狀態監聽器) 兩種主流方案,提供完整實現代碼、權限配置與優缺點分析。

核心邏輯:狀態機判斷

電話三態:

  • CALL_STATE_IDLE:閒置(掛斷、無來電)
  • CALL_STATE_RINGING:響鈴中
  • CALL_STATE_OFFHOOK:通話中(接聽、撥出)

未接來電判斷條件:上一狀態 == RINGING 且 當前狀態 == IDLE(響鈴後直接變閒置,未經 OFFHOOK)。


方案一:BroadcastReceiver(系統級廣播)

1. 權限配置

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

<receiver android:name=".receiver.PhoneStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE"/>
    </intent-filter>
</receiver>

2. 實現代碼

public class PhoneStateReceiver extends BroadcastReceiver {
    // 靜態變量記錄上一狀態(進程存活期間有效)
    private static int lastCallState = TelephonyManager.CALL_STATE_IDLE;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (!TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) return;

        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        int currentState = tm.getCallState();

        Log.d("PhoneState", "currentState=" + currentState);

        // 核心判斷邏輯
        if (lastCallState == TelephonyManager.CALL_STATE_RINGING 
                && currentState == TelephonyManager.CALL_STATE_IDLE) {
            Toast.makeText(context, "有未接來電", Toast.LENGTH_SHORT).show();
            // TODO: 發送通知、記錄日誌、上報服務器等業務邏輯
        }

        lastCallState = currentState;  // 更新狀態
    }
}

3. 特點

  • ✅ 靜態註冊:應用未啟動也能接收,適合系統級監聽
  • ✅ 進程級持久:靜態變量跨組件生命週期
  • ⚠️ Android 8.0+ 隱式廣播限制,需動態註冊或使用 JobScheduler
  • ⚠️ 無法區分「用戶主動拒接」與「未接聽」

方案二:PhoneStateListener(回調式監聽)

1. 註冊監聽

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// 傳入自定義 Listener,監聽 CALL_STATE 變化
tm.listen(new CallStateListener(this), PhoneStateListener.LISTEN_CALL_STATE);

2. 實現代碼

public class CallStateListener extends PhoneStateListener {
    private static int lastState = TelephonyManager.CALL_STATE_IDLE;
    private Context context;

    public CallStateListener(Context context) {
        this.context = context;
    }

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        Log.d("CallState", "state=" + state + ", number=" + incomingNumber);

        // 同樣的狀態機判斷
        if (lastState == TelephonyManager.CALL_STATE_RINGING 
                && state == TelephonyManager.CALL_STATE_IDLE) {
            Toast.makeText(context, "CallStateListener 有未接來電", Toast.LENGTH_SHORT).show();
        }
        lastState = state;
    }
}

3. 特點

  • ✅ 輕量級:無需清單註冊,隨 Activity/Service 生命週期
  • ✅ 可獲取來電號碼:incomingNumber 參數直接提供
  • ❌ 進程死則監聽失效:不適合後台長駐監聽
  • ❌ 同樣無法區分主動掛斷與未接

方案對比與選型建議

維度BroadcastReceiverPhoneStateListener
生命週期進程級(靜態註冊)組件級(動態註冊)
應用未啟動時✅ 能接收❌ 不能接收
獲取來電號碼需額外從 Intent 解析✅ 回調直接提供
Android 8.0+ 適配需動態註冊或豁免無影響
適用場景系統級監控、來電記錄、攔截應用內顯示來電 UI、即時響應

進階:區分「未接」與「拒接」

原生 API 無法直接區分。常見變通方案:

  1. 檢測通話時長:CALL_STATE_OFFHOOK 後極短時間進入 IDLE 可能為拒接
  2. 監聽音頻焦點/鈴聲:鈴聲播放中斷模式分析
  3. 系統調用記錄:查詢 CallLog.Calls 類型字段(MISSED vs REJECTED),需權限

權限演進提示

  • Android 6.0+:READ_PHONE_STATE 屬於危險權限,需運行時申請
  • Android 9.0+:讀取通話記錄需額外 READ_CALL_LOG
  • Android 10+:後台獲取電話狀態受限,建議配合 Foreground Service

選型原則:後台長駐監聽用 BroadcastReceiver(動態註冊),前台即時響應用 PhoneStateListener。實際項目常組合使用:BroadcastReceiver 負責喚醒應用,PhoneStateListener 負責前台交互。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved