我觉得写文章就得写得有用一些的,必须要有自己的思想,关于来电去电监听将按照下面三个问题展开

1、监听来电去电有什么用?

2、怎么监听,来电去电监听方式一样吗?

3、实战,有什么需要特别注意地方?


一、监听来电去电能干什么

1、能够对监听到的电话做个标识,告诉用户这个电话是诈骗、推销、广告什么的

2、能够针对那些特殊的电话进行自动挂断,避免打扰到用户

二、来电去电的监听方式(不一样的方式)

2.1 来去电监听方式一(PhoneStateListener)

  来电监听是使用PhoneStateListener类,使用方式是,将PhoneStateListener对象(一般是自己继承PhoneStateListener类完成一些封装)注册到系统电话管理服务中去(TelephonyManager

  然后通过PhoneStateListener的回调方法onCallStateChanged(int state, String incomingNumber) 实现来电的监听 (详细实现可以参考后面给出的拓展阅读部分)

注册监听

  1. private void registerPhoneStateListener() {
  2. CustomPhoneStateListener customPhoneStateListener = new CustomPhoneStateListener(this);
  3. TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  4. if (telephonyManager != null) {
  5. telephonyManager.listen(customPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
  6. }
  7. }

PhoneStateListener的onCallStateChanged方法监听来电状态

  1. package com.phone.listen;
  2.  
  3. import android.content.Context;
  4. import android.telephony.PhoneStateListener;
  5. import android.telephony.ServiceState;
  6. import android.telephony.TelephonyManager;
  7. import android.util.Log;
  8.  
  9. /**
  10. * 来去电监听
  11. */
  12. public class CustomPhoneStateListener extends PhoneStateListener {
  13.  
  14. private Context mContext;
  15.  
  16. public CustomPhoneStateListener(Context context) {
  17. mContext = context;
  18. }
  19.  
  20. @Override
  21. public void onServiceStateChanged(ServiceState serviceState) {
  22. super.onServiceStateChanged(serviceState);
  23. Log.d(PhoneListenService.TAG, "CustomPhoneStateListener onServiceStateChanged: " + serviceState);
  24. }
  25.  
  26. @Override
  27. public void onCallStateChanged(int state, String incomingNumber) {
  28. Log.d(PhoneListenService.TAG, "CustomPhoneStateListener state: "
    + state + " incomingNumber: " + incomingNumber);
  29. switch (state) {
  30. case TelephonyManager.CALL_STATE_IDLE: // 电话挂断
  31. break;
  32. case TelephonyManager.CALL_STATE_RINGING: // 电话响铃
  33. Log.d(PhoneListenService.TAG, "CustomPhoneStateListener onCallStateChanged endCall");
  34. HangUpTelephonyUtil.endCall(mContext);
  35. break;
  36. case TelephonyManager.CALL_STATE_OFFHOOK: // 来电接通 或者 去电,去电接通 但是没法区分
  37. break;
  38. }
  39. }
  40. }

三种状态源码解释

  1. /** Device call state: No activity. */
  2. public static final int CALL_STATE_IDLE = 0; // 电话挂断
  3. /** Device call state: Ringing. A new call arrived and is
  4. * ringing or waiting. In the latter case, another call is
  5. * already active. */
  6. public static final int CALL_STATE_RINGING = 1; // 来电响铃
  7. /** Device call state: Off-hook. At least one call exists
  8. * that is dialing, active, or on hold, and no calls are ringing
  9. * or waiting. */
  10. public static final int CALL_STATE_OFFHOOK = 2; // 来电接通 或者 去电拨号 但是没法区分出来

2.2 来去电方式二(广播监听

  1. <receiver android:name=".PhoneStateReceiver"
  2. android:enabled="true"
  3. android:process=":PhoneListenService">
  4. <intent-filter>
  5. <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
  6. <action android:name="android.intent.action.PHONE_STATE" />
  7. </intent-filter>
  8. </receiver>
  1. package com.phone.listen;
  2.  
  3. import android.content.BroadcastReceiver;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.telephony.TelephonyManager;
  7. import android.util.Log;
  8.  
  9. /**
  10. * Created by popfisher on 2017/11/6.
  11. */
  12.  
  13. public class PhoneStateReceiver extends BroadcastReceiver {
  14.  
  15. @Override
  16. public void onReceive(Context context, Intent intent) {
  17. String action = intent.getAction();
  18. Log.d(PhoneListenService.TAG, "PhoneStateReceiver action: " + action);
  19.  
  20. String resultData = this.getResultData();
  21. Log.d(PhoneListenService.TAG, "PhoneStateReceiver getResultData: " + resultData);
  22.  
  23. if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
  24. // 去电,可以用定时挂断
  25. // 双卡的手机可能不走这个Action
  26. String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
  27. Log.d(PhoneListenService.TAG, "PhoneStateReceiver EXTRA_PHONE_NUMBER: " + phoneNumber);
  28. } else {
  29. // 来电去电都会走
  30. // 获取当前电话状态
  31. String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
  32. Log.d(PhoneListenService.TAG, "PhoneStateReceiver onReceive state: " + state);
  33.  
  34. // 获取电话号码
  35. String extraIncomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
  36. Log.d(PhoneListenService.TAG, "PhoneStateReceiver onReceive extraIncomingNumber: " + extraIncomingNumber);
  37.  
  38. if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)) {
  39. Log.d(PhoneListenService.TAG, "PhoneStateReceiver onReceive endCall");
  40. HangUpTelephonyUtil.endCall(context);
  41. }
  42. }
  43. }
  44. }

三、实战,有什么需要特别注意地方

3.1 双卡双待的手机怎么获取

  对于双卡手机,每张卡都对应一个Service和一个PhoneStateListener,需要给每个服务注册自己的PhoneStateListener,服务的名称还会有点变化,厂商可能会修改

  1. public ArrayList<String> getMultSimCardInfo() {
  2. // 获取双卡的信息,这个也是经验尝试出来的,不知道其他厂商有什么坑
  3. ArrayList<String> phoneServerList = new ArrayList<String>();
  4. for(int i = 1; i < 3; i++) {
  5. try {
  6. String phoneServiceName;
  7. if (MiuiUtils.isMiuiV6()) {
  8. phoneServiceName = "phone." + String.valueOf(i-1);
  9. } else {
  10. phoneServiceName = "phone" + String.valueOf(i);
  11. }
  12.  
  13. // 尝试获取服务看是否能获取到
  14. IBinder iBinder = ServiceManager.getService(phoneServiceName);
  15. if(iBinder == null) continue;
  16. ITelephony iTelephony = ITelephony.Stub.asInterface(iBinder);
  17. if(iTelephony == null) continue;
  18. phoneServerList.add(phoneServiceName);
  19. } catch(Exception e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. // 这个是默认的
  24. phoneServerList.add(Context.TELEPHONY_SERVICE);
  25. return phoneServerList;
  26. }

3.2 挂断电话

  挂断电话使用系统服务提供的接口去挂断,但是挂断电话是个并不能保证成功的方法,所以会有多种方式挂断同时使用,下面提供

  1. package com.phone.listen;
  2.  
  3. import android.content.Context;
  4. import android.os.RemoteException;
  5. import android.telephony.TelephonyManager;
  6.  
  7. import com.android.internal.telephony.ITelephony;
  8.  
  9. import java.lang.reflect.InvocationTargetException;
  10. import java.lang.reflect.Method;
  11. import java.util.concurrent.Executor;
  12. import java.util.concurrent.Executors;
  13.  
  14. /**
  15. * 封装挂断电话接口
  16. */
  17. public class HangUpTelephonyUtil {
  18. public static boolean endCall(Context context) {
  19. boolean callSuccess = false;
  20. ITelephony telephonyService = getTelephonyService(context);
  21. try {
  22. if (telephonyService != null) {
  23. callSuccess = telephonyService.endCall();
  24. }
  25. } catch (RemoteException e) {
  26. e.printStackTrace();
  27. } catch (Exception e){
  28. e.printStackTrace();
  29. }
  30. if (callSuccess == false) {
  31. Executor eS = Executors.newSingleThreadExecutor();
  32. eS.execute(new Runnable() {
  33. @Override
  34. public void run() {
  35. disconnectCall();
  36. }
  37. });
  38. callSuccess = true;
  39. }
  40. return callSuccess;
  41. }
  42.  
  43. private static ITelephony getTelephonyService(Context context) {
  44. TelephonyManager telephonyManager = (TelephonyManager)
                  context.getSystemService(Context.TELEPHONY_SERVICE);
  45. Class clazz;
  46. try {
  47. clazz = Class.forName(telephonyManager.getClass().getName());
  48. Method method = clazz.getDeclaredMethod("getITelephony");
  49. method.setAccessible(true);
  50. return (ITelephony) method.invoke(telephonyManager);
  51. } catch (ClassNotFoundException e) {
  52. e.printStackTrace();
  53. } catch (NoSuchMethodException e) {
  54. e.printStackTrace();
  55. } catch (IllegalArgumentException e) {
  56. e.printStackTrace();
  57. } catch (IllegalAccessException e) {
  58. e.printStackTrace();
  59. } catch (InvocationTargetException e) {
  60. e.printStackTrace();
  61. }
  62. return null;
  63. }
  64.  
  65. private static boolean disconnectCall() {
  66. Runtime runtime = Runtime.getRuntime();
  67. try {
  68. runtime.exec("service call phone 5 \n");
  69. } catch (Exception exc) {
  70. exc.printStackTrace();
  71. return false;
  72. }
  73. return true;
  74. }
  75.  
  76. // 使用endCall挂断不了,再使用killCall反射调用再挂一次
  77. public static boolean killCall(Context context) {
  78. try {
  79. // Get the boring old TelephonyManager
  80. TelephonyManager telephonyManager = (TelephonyManager)
                context.getSystemService(Context.TELEPHONY_SERVICE);
  81.  
  82. // Get the getITelephony() method
  83. Class classTelephony = Class.forName(telephonyManager.getClass().getName());
  84. Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
  85.  
  86. // Ignore that the method is supposed to be private
  87. methodGetITelephony.setAccessible(true);
  88.  
  89. // Invoke getITelephony() to get the ITelephony interface
  90. Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);
  91.  
  92. // Get the endCall method from ITelephony
  93. Class telephonyInterfaceClass = Class.forName(telephonyInterface.getClass().getName());
  94. Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");
  95.  
  96. // Invoke endCall()
  97. methodEndCall.invoke(telephonyInterface);
  98. } catch (Exception ex) { // Many things can go wrong with reflection calls
  99. return false;
  100. }
  101. return true;
  102. }
  103. }

  ITelephony接口在layoutlib.jar包中,需要导入 android sdk目录\platforms\android-8\data\layoutlib.jar

挂断电话需要权限

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

3.3 监听来去电状态放到后台服务(独立进程)

  1. <service android:name=".PhoneListenService"
  2. android:label="Android来电监听"
  3. android:process=":PhoneListenService"/>

来去电监听Service

  1. package com.phone.listen;
  2.  
  3. import android.app.Service;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.os.IBinder;
  7. import android.telephony.PhoneStateListener;
  8. import android.telephony.TelephonyManager;
  9. import android.util.Log;
  10.  
  11. /**
  12. * 来去电监听服务
  13. */
  14. public class PhoneListenService extends Service {
  15.  
  16. public static final String TAG = PhoneListenService.class.getSimpleName();
  17.  
  18. public static final String ACTION_REGISTER_LISTENER = "action_register_listener";
  19.  
  20. @Override
  21. public void onCreate() {
  22. super.onCreate();
  23. Log.d(TAG, "onCreate");
  24. }
  25.  
  26. @Override
  27. public int onStartCommand(Intent intent, int flags, int startId) {
  28. Log.d(TAG, "onStartCommand action: " + intent.getAction() +
              " flags: " + flags + " startId: " + startId);
  29. String action = intent.getAction();
  30. if (action.equals(ACTION_REGISTER_LISTENER)) {
  31. registerPhoneStateListener();
  32. }
  33. return super.onStartCommand(intent, flags, startId);
  34. }
  35.  
  36. private void registerPhoneStateListener() {
  37. CustomPhoneStateListener customPhoneStateListener = new CustomPhoneStateListener(this);
  38. TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  39. if (telephonyManager != null) {
  40. telephonyManager.listen(customPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
  41. }
  42. }
  43. }

3.4 整体配置文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.phone.listen">
  4. <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  5. <uses-permission android:name="android.permission.CALL_PHONE" />
  6. <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
  7.  
  8. <application
  9. android:allowBackup="true"
  10. android:icon="@mipmap/ic_launcher"
  11. android:label="@string/app_name"
  12. android:supportsRtl="true"
  13. android:theme="@style/AppTheme">
  14. <activity android:name=".MainActivity">
  15. <intent-filter>
  16. <action android:name="android.intent.action.MAIN" />
  17. <category android:name="android.intent.category.LAUNCHER" />
  18. </intent-filter>
  19. </activity>
  20.  
  21. <service android:name=".PhoneListenService"
  22. android:label="Android来电监听"
  23. android:process=":PhoneListenService"/>
  24.  
  25. <receiver android:name=".PhoneStateReceiver"
  26. android:enabled="true"
  27. android:process=":PhoneListenService">
  28. <intent-filter>
  29. <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
  30. <action android:name="android.intent.action.PHONE_STATE" />
  31. </intent-filter>
  32. </receiver>
  33. </application>
  34.  
  35. </manifest>

3.5 Github示例

https://github.com/PopFisher/PhoneStateListen

拓展阅读:

这篇文章重点从整体框架机制方面来介绍电话监听

http://www.cnblogs.com/bastard/archive/2012/11/23/2784559.html

这篇文章重点介绍一些api方法已经变量的含义

http://blog.csdn.net/skiffloveblue/article/details/7491618

Android来电监听和去电监听的更多相关文章

  1. Android来电、去电监听

    Android手机中添加手机来电的状态,使用PhoneStateListener来监听. TelephonyManager telephonyManager = (TelephonyManager) ...

  2. Android中Button的五种监听事件

    简单聊一下Android中Button的五种监听事件: 1.在布局文件中为button添加onClick属性,Activity实现其方法2.匿名内部类作为事件监听器类3.内部类作为监听器4.Activ ...

  3. DownEditTextView【自定义Edittext对Android 软键盘向下的监听】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 记录自定义EditText控件实现监听软键盘隐藏事件的功能.基本上和参考资料相同. 效果图    代码分析 自定义EditText子 ...

  4. Oracle静态监听与动态监听概念全解析

    基于11g,linux5.5做出的测试,单实例数据库做出的测试. 1.注册 Instance到监听器去注册自己的Instance_name与ORACLE_HOME,还可以选择添加global_dbna ...

  5. Oracle LISTENER 主机名修改为IP地址后LISTENER无法监听到实例 oracle监听错误与hosts文件配置

    为什么listener.ora文件里面HOST后面到底应该输入IP地址还是主机名.我的经验告诉我,这边最好使用主机名.很多的时候,一个机器绑定的不只一个IP地址,如HOST后面是IP地址,那么ORAC ...

  6. WCF-ServiceEndpoint的监听地址与监听模式

    ServiceEndpoint具有一个可读可写的ListenUri属性,该属性表示服务端终结点的物理监听地址,该地址默认和终结点逻辑地址一致(即ServiceEndpoint的Uri).对于客户端来说 ...

  7. vue watch 深度监听以及立即监听

    vue watch对象可以监听数据,数据发生变化,处理函数 watch虽可以监听,但只是浅监听,只监听数据第一层或者第二层.比如对于整个对象的监听,需要用到深度监听 vm.$watch('obj',f ...

  8. js实现事件监听与阻止监听传播

    监听事件: 使用attachEvent(用于IE)和addEventListener(用于谷歌.火狐)时则可以实现多个事件处理函数的调用 1.下面都是dom对象的方法,可以实现一种事件绑定多个事件处理 ...

  9. oracle 11g rac修改监听端口(远程监听和本地监听)

    转至:https://www.cnblogs.com/yj411511/p/12459533.html 目录 1.修改远程监听端口 1.1 查看远程监听状态 1.2 修改SCAN listener端口 ...

随机推荐

  1. SQL Server 2014 新特性——内存数据库

    SQL Server 2014 新特性——内存数据库 目录 SQL Server 2014 新特性——内存数据库 简介: 设计目的和原因: 专业名词 In-Memory OLTP不同之处 内存优化表 ...

  2. UWP开发必备以及常用知识点总结

    一直在学UWP,一直在写Code,自己到达了什么水平?还有多少东西需要学习才能独挡一面?我想对刚接触UWP的开发者都有这种困惑,偶尔停下来总结分析一下还是很有收获的! 以下内容是自己开发中经常遇到的一 ...

  3. 【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57e3a3bc42eb88da6d4be143 作者:王赛 1. 前言 Andr ...

  4. required

    required,这是HTML5中的一个新属性:这是HTML5中input元素中的一个属性. required译为必须的,在input元素中应用这一属性,就表示这一input元素节点是必填的或者必选的 ...

  5. Oracle-BPM安装详解

    H3 BPM安装包括两个部分,基础工作包括安装IIS..net Freamwork基础框架.安装完成之后,主要配置安装包括数据库,H3 BPM 程序.下面详细介绍Oracle与H3 BPM对接安装的整 ...

  6. SharePoint 2016 入门视频教程

    之前一直有朋友让自己录一些SharePoint的入门视频,之前没有太多时间,一个巧合的机会收到CSDN学院的邮件,可以在CSDN上发布视频教程,自己就录了一些.说起录视频也是蛮辛苦的,每天下班吃完饭要 ...

  7. 使用HEXO快速建站

    先安好npm,请参照:http://max.cszi.com/archives/482 打开网站:https://hexo.io/   npm install hexo-cli -g hexo ini ...

  8. 理解JavaScript中的“this”

    对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑.本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“t ...

  9. [译]ZOOKEEPER RECIPES-Locks

    锁 全局式分布式锁要求任何时刻没有两个客户端会获得同一个锁对象,这可以通过使用ZooKeeper实现.像优先级队列一样,首先需要定义一个锁节点. 在ZooKepeer的发布中src/recipes/l ...

  10. iOS App引导页功能实现

    一.写作原因 以前都没有想着来写点东西,今天遇到件事情让我决定每次还是要做记录.因为以前自己可以轻松的完成pod spec的配置,但是今天在做的时候还是忘了遇到了很多坑.pod spec配置遇到的坑不 ...