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

Android 監聽未接來電完整實作:PhoneStateListener + BroadcastReceiver + 權限配置

     編輯:Java 相關知識

Android 系統未提供直接的「未接來電」廣播,需組合 PhoneStateListener 監聽通話狀態變化 (RINGING → IDLE) 判斷未接來電。本文提供完整實作:狀態監聽器、廣播接收器啟動監聽、Manifest 註冊與權限聲明。

核心架構

  1. CallListener:繼承 PhoneStateListener,覆寫 onCallStateChanged() 偵測 CALL_STATE_RINGING → CALL_STATE_IDLE 轉換
  2. CallReceiver:繼承 BroadcastReceiver,接收 PHONE_STATE 廣播,向 TelephonyManager 註冊 CallListener
  3. Manifest:聲明 READ_PHONE_STATE 權限、註冊 CallReceiver (高優先級)

完整程式碼實作

1. CallListener.java — 狀態變化偵測

public class CallListener extends PhoneStateListener {
    private static final String TAG = "MissedCallDetector";
    private static int lastState = TelephonyManager.CALL_STATE_IDLE;
    private final Context context;

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

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        Log.d(TAG, "State changed: " + state + " number: " + incomingNumber);

        // 關鍵判斷:上一狀態響鈴,當前狀態閒置 = 未接來電
        if (lastState == TelephonyManager.CALL_STATE_RINGING &&
            state == TelephonyManager.CALL_STATE_IDLE) {
            handleMissedCall(incomingNumber);
        }
        lastState = state;
    }

    private void handleMissedCall(String number) {
        Log.i(TAG, "Missed call from: " + number);
        // TODO: 發送通知、記錄日誌、呼叫 API、啟動 Service 等業務邏輯
        // 注意:這裡在主執行緒,耗時操作請用 Handler/Thread/Service
    }
}

2. CallReceiver.java — 廣播啟動監聽

public class CallReceiver extends BroadcastReceiver {
    private static final String TAG = "MissedCallDetector";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "PHONE_STATE broadcast received");
        String action = intent.getAction();
        if (!TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) return;

        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        CallListener listener = new CallListener(context);
        // 僅監聽通話狀態,避免不必要的回調
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

        // 可選:從意圖取得來電號碼 (部分 ROM 需此補充)
        Bundle extras = intent.getExtras();
        if (extras != null) {
            String number = extras.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
            Log.d(TAG, "Incoming number from broadcast: " + number);
        }
    }
}

3. AndroidManifest.xml — 權限與接收器註冊

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.missedcall">

    <!-- 必須權限:讀取電話狀態 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application ...>
        <!-- 高優先級確保優先接收 -->
        <receiver
            android:name=".CallReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="1000">
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Android 版本相容性關鍵點

版本影響應對
Android 6.0 (API 23)+執行時權限需動態請求 READ_PHONE_STATE,用戶拒絕則無法監聽
Android 9.0 (API 28)+限制後台接收隱式廣播PHONE_STATE 為系統豁免廣播,靜態註冊仍生效
Android 10 (API 29)+限制讀取裝置識別碼/電話號碼來電號碼可能為空,需 READ_CALL_LOG 權限配合 CallLog 查詢
Android 11 (API 30)+前台服務類型限制若需長期監聽,建議改用 Foreground Service + FOREGROUND_SERVICE_PHONE_CALL

進階:長期監聽的 Foreground Service 方案

BroadcastReceiver 僅在來電時被喚醒,若 App 進程被殺死可能漏接。生產級方案:

// Manifest
<service android:name=".CallMonitorService"
    android:foregroundServiceType="phoneCall"
    android:exported="false" />

// CallMonitorService.java
public class CallMonitorService extends Service {
    private TelephonyManager tm;
    private CallListener listener;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        createNotificationChannel();
        startForeground(1, buildNotification());
        tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        listener = new CallListener(this);
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
        return START_STICKY;
    }

    @Override public void onDestroy() {
        if (tm != null && listener != null) tm.listen(listener, PhoneStateListener.LISTEN_NONE);
        super.onDestroy();
    }
    // ... Notification 建立略
}

常見問題排查

  • 收不到廣播:檢查權限、Manifest 註冊、優先級、是否被電源管理白名單攔截
  • 號碼為空:Android 10+ 需 READ_CALL_LOG 權限查詢 CallLog.Calls;雙卡設備可能需 SubscriptionManager
  • 重複觸發:lastState 為 static 變數,進程重啟會重置,可持久化至 SharedPreferences
  • 雙卡支援:API 30+ 使用 TelephonyManager.createForSubscriptionId(subId) 區分卡槽

原文標題錯誤提示:原標題為「struts2單個文件上傳的兩種完成方法」,但實際內容為 Android 未接來電監聽。本文已依實際內容重寫。

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