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

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

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

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


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

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

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

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

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

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

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

注册监听

private void registerPhoneStateListener() {
CustomPhoneStateListener customPhoneStateListener = new CustomPhoneStateListener(this);
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
telephonyManager.listen(customPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}

PhoneStateListener的onCallStateChanged方法监听来电状态

package com.phone.listen;

import android.content.Context;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log; /**
* 来去电监听
*/
public class CustomPhoneStateListener extends PhoneStateListener { private Context mContext; public CustomPhoneStateListener(Context context) {
mContext = context;
} @Override
public void onServiceStateChanged(ServiceState serviceState) {
super.onServiceStateChanged(serviceState);
Log.d(PhoneListenService.TAG, "CustomPhoneStateListener onServiceStateChanged: " + serviceState);
} @Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.d(PhoneListenService.TAG, "CustomPhoneStateListener state: "
+ state + " incomingNumber: " + incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: // 电话挂断
break;
case TelephonyManager.CALL_STATE_RINGING: // 电话响铃
Log.d(PhoneListenService.TAG, "CustomPhoneStateListener onCallStateChanged endCall");
HangUpTelephonyUtil.endCall(mContext);
break;
case TelephonyManager.CALL_STATE_OFFHOOK: // 来电接通 或者 去电,去电接通 但是没法区分
break;
}
}
}

三种状态源码解释

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

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

<receiver android:name=".PhoneStateReceiver"
android:enabled="true"
android:process=":PhoneListenService">

<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
package com.phone.listen;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.util.Log; /**
* Created by popfisher on 2017/11/6.
*/ public class PhoneStateReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(PhoneListenService.TAG, "PhoneStateReceiver action: " + action); String resultData = this.getResultData();
Log.d(PhoneListenService.TAG, "PhoneStateReceiver getResultData: " + resultData); if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
// 去电,可以用定时挂断
// 双卡的手机可能不走这个Action
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Log.d(PhoneListenService.TAG, "PhoneStateReceiver EXTRA_PHONE_NUMBER: " + phoneNumber);
} else {
// 来电去电都会走
// 获取当前电话状态
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
Log.d(PhoneListenService.TAG, "PhoneStateReceiver onReceive state: " + state); // 获取电话号码
String extraIncomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d(PhoneListenService.TAG, "PhoneStateReceiver onReceive extraIncomingNumber: " + extraIncomingNumber); if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)) {
Log.d(PhoneListenService.TAG, "PhoneStateReceiver onReceive endCall");
HangUpTelephonyUtil.endCall(context);
}
}
}
}

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

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

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

public ArrayList<String> getMultSimCardInfo() {
// 获取双卡的信息,这个也是经验尝试出来的,不知道其他厂商有什么坑
ArrayList<String> phoneServerList = new ArrayList<String>();
for(int i = 1; i < 3; i++) {
try {
String phoneServiceName;
if (MiuiUtils.isMiuiV6()) {
phoneServiceName = "phone." + String.valueOf(i-1);
} else {
phoneServiceName = "phone" + String.valueOf(i);
} // 尝试获取服务看是否能获取到
IBinder iBinder = ServiceManager.getService(phoneServiceName);
if(iBinder == null) continue; ITelephony iTelephony = ITelephony.Stub.asInterface(iBinder);
if(iTelephony == null) continue
; phoneServerList.add(phoneServiceName);
} catch(Exception e) {
e.printStackTrace();
}
}
// 这个是默认的
phoneServerList.add(Context.TELEPHONY_SERVICE);
return phoneServerList;
}

3.2 挂断电话

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

package com.phone.listen;

import android.content.Context;
import android.os.RemoteException;
import android.telephony.TelephonyManager; import com.android.internal.telephony.ITelephony; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors; /**
* 封装挂断电话接口
*/
public class HangUpTelephonyUtil {
public static boolean endCall(Context context) {
boolean callSuccess = false;
ITelephony telephonyService = getTelephonyService(context);
try {
if (telephonyService != null) {
callSuccess = telephonyService.endCall();
}
} catch (RemoteException e) {
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
if (callSuccess == false) {
Executor eS = Executors.newSingleThreadExecutor();
eS.execute(new Runnable() {
@Override
public void run() {
disconnectCall();
}
});
callSuccess = true;
}
return callSuccess;
} private static ITelephony getTelephonyService(Context context) {
TelephonyManager telephonyManager = (TelephonyManager)
              context.getSystemService(Context.TELEPHONY_SERVICE);
Class clazz;
try {
clazz = Class.forName(telephonyManager.getClass().getName());
Method method = clazz.getDeclaredMethod("getITelephony"
);
method.setAccessible(true);
return (ITelephony) method.invoke(telephonyManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
} private static boolean disconnectCall() {
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec("service call phone 5 \n");
} catch (Exception exc) {
exc.printStackTrace();
return false;
}
return true;
} // 使用endCall挂断不了,再使用killCall反射调用再挂一次
public static boolean killCall(Context context) {
try {
// Get the boring old TelephonyManager
TelephonyManager telephonyManager = (TelephonyManager)
            context.getSystemService(Context.TELEPHONY_SERVICE); // Get the getITelephony() method
Class classTelephony = Class.forName(telephonyManager.getClass().getName());
Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony"); // Ignore that the method is supposed to be private
methodGetITelephony.setAccessible(true); // Invoke getITelephony() to get the ITelephony interface
Object telephonyInterface = methodGetITelephony.invoke(telephonyManager); // Get the endCall method from ITelephony
Class telephonyInterfaceClass = Class.forName(telephonyInterface.getClass().getName());
Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall"); // Invoke endCall()
methodEndCall.invoke(telephonyInterface);
} catch (Exception ex) { // Many things can go wrong with reflection calls
return false;
}
return true;
}
}

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

挂断电话需要权限

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

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

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

来去电监听Service

package com.phone.listen;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log; /**
* 来去电监听服务
*/
public class PhoneListenService extends Service { public static final String TAG = PhoneListenService.class.getSimpleName(); public static final String ACTION_REGISTER_LISTENER = "action_register_listener"; @Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand action: " + intent.getAction() +
          " flags: " + flags + " startId: " + startId);
String action = intent.getAction();
if (action.equals(ACTION_REGISTER_LISTENER)) {
registerPhoneStateListener();
}
return super.onStartCommand(intent, flags, startId);
} private void registerPhoneStateListener() {
CustomPhoneStateListener customPhoneStateListener = new CustomPhoneStateListener(this);
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
telephonyManager.listen(customPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}

3.4 整体配置文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.phone.listen">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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> <service android:name=".PhoneListenService"
android:label="Android来电监听"
android:process=":PhoneListenService"/> <receiver android:name=".PhoneStateReceiver"
android:enabled="true"
android:process=":PhoneListenService">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
</application> </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. Dapper扩展之~~~Dapper.Contrib

    平台之大势何人能挡? 带着你的Net飞奔吧!http://www.cnblogs.com/dunitian/p/4822808.html#skill 上一篇文章:Dapper逆天入门~强类型,动态类型 ...

  2. Matlab slice方法和包络法绘制三维立体图

    前言:在地球物理勘探,流体空间分布等多种场景中,定位空间点P(x,y,x)的物理属性值Q,并绘制三维空间分布图,对我们洞察空间场景有十分重要的意义. 1. 三维立体图的基本要件: 全空间网格化 网格节 ...

  3. SQL Server-聚焦使用视图若干限制/建议、视图查询性能问题,你懵逼了?(二十五)

    前言 上一节我们简单讲述了表表达式的4种类型,这一系列我们来讲讲使用视图的限制,简短的内容,深入的理解,Always to review the basics. 避免在视图中使用ORDER BY 上一 ...

  4. Mac OS 使用 Vagrant 管理虚拟机(VirtualBox)

    Vagrant(官网.github)是一款构建虚拟开发环境的工具,支持 Window,Linux,Mac OS,Vagrant 中的 Boxes 概念类似于 Docker(实质是不同的),你可以把它看 ...

  5. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

  6. MySQL 系列(四)主从复制、备份恢复方案生产环境实战

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  7. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  8. SQL 数据优化索引建suo避免全表扫描

    首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检 ...

  9. Linux配置防火墙 开启80端口的方法

    命令行输入: vi /etc/sysconfig/iptables 将 -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT ...

  10. 聊聊从web session的共享到可扩展缓存设计

    先从web session的共享说起   许多系统需要提供7*24小时服务,这类系统肯定需要考虑灾备问题,单台服务器如果宕机可能无法立马恢复使用,这必定影响到服务.这个问题对于系统规模来说,从小到大可 ...