Android来电监听和去电监听
我觉得写文章就得写得有用一些的,必须要有自己的思想,关于来电去电监听将按照下面三个问题展开
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来电监听和去电监听的更多相关文章
- Android来电、去电监听
Android手机中添加手机来电的状态,使用PhoneStateListener来监听. TelephonyManager telephonyManager = (TelephonyManager) ...
- Android中Button的五种监听事件
简单聊一下Android中Button的五种监听事件: 1.在布局文件中为button添加onClick属性,Activity实现其方法2.匿名内部类作为事件监听器类3.内部类作为监听器4.Activ ...
- DownEditTextView【自定义Edittext对Android 软键盘向下的监听】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 记录自定义EditText控件实现监听软键盘隐藏事件的功能.基本上和参考资料相同. 效果图 代码分析 自定义EditText子 ...
- Oracle静态监听与动态监听概念全解析
基于11g,linux5.5做出的测试,单实例数据库做出的测试. 1.注册 Instance到监听器去注册自己的Instance_name与ORACLE_HOME,还可以选择添加global_dbna ...
- Oracle LISTENER 主机名修改为IP地址后LISTENER无法监听到实例 oracle监听错误与hosts文件配置
为什么listener.ora文件里面HOST后面到底应该输入IP地址还是主机名.我的经验告诉我,这边最好使用主机名.很多的时候,一个机器绑定的不只一个IP地址,如HOST后面是IP地址,那么ORAC ...
- WCF-ServiceEndpoint的监听地址与监听模式
ServiceEndpoint具有一个可读可写的ListenUri属性,该属性表示服务端终结点的物理监听地址,该地址默认和终结点逻辑地址一致(即ServiceEndpoint的Uri).对于客户端来说 ...
- vue watch 深度监听以及立即监听
vue watch对象可以监听数据,数据发生变化,处理函数 watch虽可以监听,但只是浅监听,只监听数据第一层或者第二层.比如对于整个对象的监听,需要用到深度监听 vm.$watch('obj',f ...
- js实现事件监听与阻止监听传播
监听事件: 使用attachEvent(用于IE)和addEventListener(用于谷歌.火狐)时则可以实现多个事件处理函数的调用 1.下面都是dom对象的方法,可以实现一种事件绑定多个事件处理 ...
- oracle 11g rac修改监听端口(远程监听和本地监听)
转至:https://www.cnblogs.com/yj411511/p/12459533.html 目录 1.修改远程监听端口 1.1 查看远程监听状态 1.2 修改SCAN listener端口 ...
随机推荐
- SQL Server 2014 新特性——内存数据库
SQL Server 2014 新特性——内存数据库 目录 SQL Server 2014 新特性——内存数据库 简介: 设计目的和原因: 专业名词 In-Memory OLTP不同之处 内存优化表 ...
- UWP开发必备以及常用知识点总结
一直在学UWP,一直在写Code,自己到达了什么水平?还有多少东西需要学习才能独挡一面?我想对刚接触UWP的开发者都有这种困惑,偶尔停下来总结分析一下还是很有收获的! 以下内容是自己开发中经常遇到的一 ...
- 【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57e3a3bc42eb88da6d4be143 作者:王赛 1. 前言 Andr ...
- required
required,这是HTML5中的一个新属性:这是HTML5中input元素中的一个属性. required译为必须的,在input元素中应用这一属性,就表示这一input元素节点是必填的或者必选的 ...
- Oracle-BPM安装详解
H3 BPM安装包括两个部分,基础工作包括安装IIS..net Freamwork基础框架.安装完成之后,主要配置安装包括数据库,H3 BPM 程序.下面详细介绍Oracle与H3 BPM对接安装的整 ...
- SharePoint 2016 入门视频教程
之前一直有朋友让自己录一些SharePoint的入门视频,之前没有太多时间,一个巧合的机会收到CSDN学院的邮件,可以在CSDN上发布视频教程,自己就录了一些.说起录视频也是蛮辛苦的,每天下班吃完饭要 ...
- 使用HEXO快速建站
先安好npm,请参照:http://max.cszi.com/archives/482 打开网站:https://hexo.io/ npm install hexo-cli -g hexo ini ...
- 理解JavaScript中的“this”
对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑.本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“t ...
- [译]ZOOKEEPER RECIPES-Locks
锁 全局式分布式锁要求任何时刻没有两个客户端会获得同一个锁对象,这可以通过使用ZooKeeper实现.像优先级队列一样,首先需要定义一个锁节点. 在ZooKepeer的发布中src/recipes/l ...
- iOS App引导页功能实现
一.写作原因 以前都没有想着来写点东西,今天遇到件事情让我决定每次还是要做记录.因为以前自己可以轻松的完成pod spec的配置,但是今天在做的时候还是忘了遇到了很多坑.pod spec配置遇到的坑不 ...