最近公司接到一个需求: 需要安卓端监听用户打出去的电话, 并记录在自己的数据库中, 也就是说不能查询通话记录现在网上的东西千篇一律, 所以写点自己的理解
需要用到的工具:
- TelephonyManager
- PhoneStateListener
- BroadcastReceiver
- Intent.ACTION_NEW_OUTGOING_CALL
- android.permission.PROCESS_OUTGOING_CALLS
- android.permission.READ_PHONE_STATE
实现方案
首先注册广播接收者去电监听, 用 TelephonyManager 监听电话状态
广播接收者代码
logger 工具 com.orhanobut:logger
- public class PhoneReceiver extends BroadcastReceiver {
- private boolean isListen = false;
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!isListen) {
- TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
- if (tm != null) {
- tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
- isListen = true;
- }
- }
- Logger.t("xxxx").d(intent.getAction());
- // 去电
- if (Objects.equals(intent.getAction(), Intent.ACTION_NEW_OUTGOING_CALL)) {
- String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
- // 这个地方只有你拨号的瞬间会调用
- Logger.t("xxxx").d("call OUT:" + phoneNumber);
- }
- }
- PhoneStateListener listener = new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- switch (state) {
- case TelephonyManager.CALL_STATE_IDLE:
- // 不管是去电还是来电通话结束都会调用, 不管是不是你挂的
- Logger.t("xxxx").d("挂断" + incomingNumber);
- break;
- case TelephonyManager.CALL_STATE_OFFHOOK:
- // 如果是来电, 这个必须点击接听按钮才会调用
- // 如果是拨打, 那么一开始就会调用, 并不是打通了之后才会调用
- Logger.t("xxxx").d("摘机状态, 接听或者拨打" + incomingNumber);
- break;
- case TelephonyManager.CALL_STATE_RINGING:
- // 只有来电的时候会调用
- Logger.t("xxxx").d("响铃: 来电号码:" + incomingNumber);
- break;
- }
- }
- };
- }
activity 中的注册代码
- private PhoneReceiver receiver;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ...
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
- receiver = new PhoneReceiver();
- registerReceiver(receiver, filter);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- ...
- unregisterReceiver(receiver);
- }
权限
Manifest 中
- <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
权限我用的 RxPermissions
- RxPermissions rxPermissions = new RxPermissions(this);
- rxPermissions.request(
- Manifest.permission.PROCESS_OUTGOING_CALLS,
- Manifest.permission.READ_PHONE_STATE,) // 权限名称, 多个权限之间逗号分隔开
- .subscribe(granted -> {
- // 在 android 6.0 之前会默认返回 true
- if (granted) {
- // 权限通过后, 检查 APP 是否需要更新, 并且直接登陆
- goLogin();
- } else {
- // 权限未通过
- AlertDialog alertDialog = new AlertDialog.Builder(this)
- .setTitle("您没有授权该权限, 请在设置中打开授权, 重新进入 APP 登录")
- .setPositiveButton("确定", (dialog, which) -> {
- Uri packageURI = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
- startActivity(intent);
- HomeApp.exitApp();
- })
- .setNegativeButton("取消", (dialog, which) -> MyApplication.exitApp()).create();
- alertDialog.setCanceledOnTouchOutside(false);
- alertDialog.setOnKeyListener((dialog, keyCode, event) -> keyCode == KeyEvent.KEYCODE_BACK);
- alertDialog.show();
- }
- });
坑点
1.TelephonyManager
首先来讲一下 TelephonyManager, 这个东西有点坑
public void listen(PhoneStateListener listener, int events)
实测, TelephonyManager 如果是在 activity 中获取的, 上面这个方法在你调用过后
PhoneStateListener 的
public void onCallStateChanged(int state, String incomingNumber)
这个方法只会被调用一次, 而如果 TelephonyManager 是通过 BroadcastReceiver 中 onReceive 的 Context 获取的那么就会多次调用
TelephonyManager 的第二个坑爹的地方是没有办法注销 listener 经过实验发现注册这个广播的 activity 挂了之后 onCallStateChanged 方法也只会被调用一次了, 所以并不用担心注册了之后需要注销所以尽量不要静态注册这个广播接收者
- <font color="red"> 错误示范 </font>
- <receiver android:name=".PhoneReceiver">
- <intent-filter>
- <action android:name="android.intent.action.PHONE_STATE"/>
- <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
- </intent-filter>
- </receiver>
- 2.PhoneStateListener
- <font color="red"> 错误示范 </font>
- // 注意, 方法必须写在 super 方法后面, 否则 incomingNumber 无法获取到值
- super.onCallStateChanged(state, incomingNumber);
首先这行代码并不需要调用, 源码父类都是空实现, 上面的注解更是无稽之谈
3.BroadcastReceiver
第一: 这个玩意里面你才能够用 TelephonyManager
第二: 这个尽量动态注册
第三:
<action android:name="android.intent.action.PHONE_STATE"/>
这个可以不需要, 你不需要用这个 action 让 Receiver 每次都收到电话状态的变化因为 PhoneStateListener 已经跟你监听了, 除非你想自己用 Receiver 写而不用 PhoneStateListener 监听 **
第四: 去电监听只会在你拨打电话号码得时候才会被回掉, 如果你想监听来电请不要像我这样写你还是需要注册 PHONE_STATE 这个 action 来初始化 PhoneStateListener
总结
网上抄手多, 想要弄清楚要么自己试, 要么看文档以上算是我记录一些坑点, 毕竟文档也写得很粗糙
来源: http://www.jianshu.com/p/b1f5b9d85d6d