【问题标题】:Broadcast receiver for Phone State changed not working电话状态的广播接收器更改不工作
【发布时间】:2018-09-17 14:18:38
【问题描述】:

我为电话状态更改创建了一个广播接收器。但广播不工作。我已经尝试了几个小时并尝试了 2,3 解决方案,但仍然无法正常工作。互联网上的其他人有相同的代码,并且对他们来说工作正常。我不知道我在哪里犯错。需要你的帮助! 这是我的清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="veclar.map.callandsmsblocking">
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>
        </activity>
        <receiver android:name=".PhoneCallReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
            </intent-filter>
        </receiver>
    </application>
</manifest> 

这是我的 PhoneCallReceiver 类

public abstract class PhoneCallReceiver extends BroadcastReceiver {

    //The receiver will be recreated whenever android feels like it.  We need a static variable to remember data between instantiations

    private static int lastState = TelephonyManager.CALL_STATE_IDLE;
    private static Date callStartTime;
    private static boolean isIncoming;
    private static String savedNumber;  //because the passed incoming is only valid in ringing

    @Override
    public void onReceive(Context context, Intent intent) {

        //We listen to two intents.  The new outgoing call only tells us of an outgoing call.  We use it to get the number.
        if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
            savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
        }
        else{
            String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
            int state = 0;
            if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
                state = TelephonyManager.CALL_STATE_IDLE;
            }
            else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
                state = TelephonyManager.CALL_STATE_OFFHOOK;
            }
            else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
                state = TelephonyManager.CALL_STATE_RINGING;
            }

            onCallStateChanged(context, state, number);
        }
    }

    //Incoming call-  goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
    //Outgoing call-  goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
    public void onCallStateChanged(Context context, int state, String number) {
        if(lastState == state){
            //No change, debounce extras
            return;
        }
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                isIncoming = true;
                callStartTime = new Date();
                savedNumber = number;

                Toast.makeText(context, "Incoming Call Ringing" , Toast.LENGTH_SHORT).show();
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                //Transition of ringing->offhook are pickups of incoming calls.  Nothing done on them
                if(lastState != TelephonyManager.CALL_STATE_RINGING){
                    isIncoming = false;
                    callStartTime = new Date();
                    Toast.makeText(context, "Outgoing Call Started" , Toast.LENGTH_SHORT).show();
                }

                break;
            case TelephonyManager.CALL_STATE_IDLE:
                //Went to idle-  this is the end of a call.  What type depends on previous state(s)
                if(lastState == TelephonyManager.CALL_STATE_RINGING){
                    //Ring but no pickup-  a miss
                    Toast.makeText(context, "Ringing but no pickup" + savedNumber + " Call time " + callStartTime +" Date " + new Date() , Toast.LENGTH_SHORT).show();
                }
                else if(isIncoming){

                    Toast.makeText(context, "Incoming " + savedNumber + " Call time " + callStartTime  , Toast.LENGTH_SHORT).show();
                }
                else{

                    Toast.makeText(context, "outgoing " + savedNumber + " Call time " + callStartTime +" Date " + new Date() , Toast.LENGTH_SHORT).show();

                }

                break;
        }

        lastState = state;
    }
}

【问题讨论】:

  • 你找到答案了吗?

标签: android broadcastreceiver android-broadcastreceiver telephony telephonymanager


【解决方案1】:

您无法再以这种方式接收 PHONE_STATE_CHANGED 广播。

来自官方 Android 开发者指南https://developer.android.com/guide/components/broadcasts

从 Android 8.0(API 级别 26)开始,系统强制 对清单声明的接收方的额外限制。

如果您的应用面向 Android 8.0 或更高版本,则无法使用清单 为大多数隐式广播声明一个接收器(广播 不要专门针对您的应用程序)。您仍然可以使用 当用户积极使用您的应用时,上下文注册的接收器。

您必须使用显式广播接收器(从您的活动注册)来接收 PHONE_STATE_CHANGED 广播。

public class ToastDisplay extends Activity {

    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(getApplicationContext(), "received", Toast.LENGTH_SHORT);
        }
    };

    @Override
    protected void onResume() {
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.PHONE_STATE");
        registerReceiver(receiver, filter);
        super.onResume();
    }

    @Override
    protected void onPause() {
        unregisterReceiver(receiver);
        super.onPause();
    }
}

此外,除了在 manifest 中声明 android.permission.READ_PHONE_STATEandroid.permission.PROCESS_OUTGOING_CALLS 等所需权限外,您还必须在运行时从用户那里显式获取这些权限。否则您将不会收到一些(大多数)系统广播。 Android 开发者指南对向用户请求权限和代码示例有很好的解释。 https://developer.android.com/training/permissions/requesting

【讨论】:

  • 您提供的链接已失效。
  • 我也尝试过显式广播接收器,但也没有用。我是这样注册的。 LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationPhoneStateReceiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED));
  • 是的,我也在动态请求,但手机没有授予此权限
  • @Junaid Bashir 请将其作为一个单独的问题发布并包含相关的代码示例。
  • @Prasath 由于 API 级别 26,对于大多数隐式广播,您可以在用户积极使用您的应用程序时接收它们。那是Android施加的限制。除非您正在运行 Foreground Service 或用户正在使用您的活动之一,否则您将无法接收大多数隐式广播。因此,我们在用户使用 Activity 时注册广播接收器(onResume),并在我们的 Activity 不在焦点时取消注册(onPause)。
【解决方案2】:

我在清单中定义的手机状态权限 (android.permission.READ_PHONE_STATE) 不够,所以当我从应用设置手动授予应用权限时,它开始接收 Phone_State 广播。我认为需要用户的运行时许可。

【讨论】:

  • 花了 5 小时并忽略了这个解决方案后,我不情愿地试了一下......它成功了!谢谢
【解决方案3】:
  1. 首先,在清单中定义权限

    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    
  2. 在应用标签内注册接收者

    <receiver android:name=".PhoneCallReceiver" >
         <intent-filter>
             <action android:name="android.intent.action.PHONE_STATE"/>
         </intent-filter>
         <intent-filter>
             <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
         </intent-filter>
     </receiver>
    
  3. 询问启动Activity的权限/Runtime Permission一般在MainActivity中

    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
    
           checkAndRequestPermissions();
    
          }
        private  boolean checkAndRequestPermissions() {
     int readPhoneState = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE);
     int read_call_log = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG);
    
     List listPermissionsNeeded = new ArrayList<>();
    
     if (readPhoneState != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.READ_PHONE_STATE);
     }
    
     if (read_call_log != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.READ_CALL_LOG);
     }
    
     if (read_call_log != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
     }
    
     if (read_call_log != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.INTERNET);
     }
    
     if (!listPermissionsNeeded.isEmpty()) {
         ActivityCompat.requestPermissions(this,
                 (String[]) listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),
                 REQUEST_ID_MULTIPLE_PERMISSIONS);
    
         return false;
     }
     return true;
    }
    
  4. 在 PhoneCallReciver 类中

     public class CallReceiver extends BroadcastReceiver{
    
    
     @Override
         public void onReceive(Context context, Intent intent) {
    
             try {
    
                 runfirstTime(context,intent);
    
             } catch (Exception ex) {
                 try {
    
                 }
                 catch (Exception e)
                 {
    
                 }
             }
         }
    
    
     private void runfirstTime(Context context, Intent intent) {
    
         TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
         MyPhoneStateListener customPhoneListener = new MyPhoneStateListener();
    
         telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
    
         Bundle bundle = intent.getExtras();
         String phone_number = bundle.getString("incoming_number");
    
         String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
         int state = 0;
    
         if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
             state = TelephonyManager.CALL_STATE_IDLE;
         }
         else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
             state = TelephonyManager.CALL_STATE_OFFHOOK;
         }
         else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
             state = TelephonyManager.CALL_STATE_RINGING;
         }
    
         if (phone_number == null || "".equals(phone_number)) {
             return;
         }
         customPhoneListener.onCallStateChanged(context, state, phone_number);
        // Here customPhoneListener is a object of the  use context,state,phone_number to get the MyCustomPhonestatelistener class Which extends PhoneStateListener class.
    
     }
    }
    

【讨论】:

  • 在我接受你的回答之前浪费了足够的时间,非常感谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多