Android系统提供了多种实现通话功能的方式,核心涉及TelecomManager和隐式Intent,以下是具体实现方案:
权限声明
在AndroidManifest.xml添加必要权限:
<uses-permissionandroid:name="android.permission.CALL_PHONE"/><uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
动态权限请求(Android6.0+)
privatevoidrequestCallPermission(){if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(this,newString[]{Manifest.permission.CALL_PHONE},REQUEST_CALL_PERMISSION);}else{makePhoneCall("13800138000");//已有权限直接拨号}}@OverridepublicvoidonRequestPermissionsResult(intcode,String[]permissions,int[]results){if(code==REQUEST_CALL_PERMISSION&&results.length>0&&results[0]==PackageManager.PERMISSION_GRANTED){makePhoneCall("13800138000");}}
显式拨号界面
使用ACTION_DIAL打开拨号盘预填充号码:
publicvoidopenDialPad(StringphoneNumber){IntentdialIntent=newIntent(Intent.ACTION_DIAL);dialIntent.setData(Uri.parse("tel:"+phoneNumber));startActivity(dialIntent);}
直接拨打电话(需CALL_PHONE权限)
通过ACTION_CALL直接发起通话:
publicvoidmakePhoneCall(StringphoneNumber){try{IntentcallIntent=newIntent(Intent.ACTION_CALL);callIntent.setData(Uri.parse("tel:"+phoneNumber));//检查双卡设备(API22+)if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP_MR1){TelecomManagertm=(TelecomManager)getSystemService(TELECOM_SERVICE);if(tm.getCallCapablePhoneAccounts().size()>1){callIntent.putExtra("android.telecom.extra.PREFERRED_PHONE_ACCOUNT",tm.getCallCapablePhoneAccounts().get(0));}}startActivity(callIntent);}catch(SecurityExceptione){Log.e("CallError","Permissiondenied:"+e.getMessage());}}
监听通话状态
publicclassCallStateMonitorextendsPhoneStateListener{@OverridepublicvoidonCallStateChanged(intstate,Stringnumber){switch(state){caseTelephonyManager.CALL_STATE_RINGING:Log.d("CallState","来电中:"+number);break;caseTelephonyManager.CALL_STATE_OFFHOOK:Log.d("CallState","通话中");break;caseTelephonyManager.CALL_STATE_IDLE:Log.d("CallState","挂断状态");break;}}}//注册监听器TelephonyManagertm=(TelephonyManager)getSystemService(TELEPHONY_SERVICE);tm.listen(newCallStateMonitor(),PhoneStateListener.LISTEN_CALL_STATE);
特殊号码处理
//拨打分机号Uriuri=Uri.parse("tel:10800"+Uri.encode("#")+"123456"+Uri.encode("#"));//发送USSD代码startActivity(newIntent(Intent.ACTION_CALL,Uri.parse("tel:123%23")));//语音信箱startActivity(newIntent(Intent.ACTION_CALL,Uri.parse("voicemail:")));
通话管理高级技巧
-
自定义通话界面:
实现ConnectionService重写onCreateOutgoingConnection
publicclassMyConnectionServiceextendsConnectionService{@OverridepublicConnectiononCreateOutgoingConnection(PhoneAccountHandleconnectionManagerPhoneAccount,ConnectionRequestrequest){Connectionconnection=newConnection(){@OverridepublicvoidonAnswer(){/接听逻辑/}@OverridepublicvoidonDisconnect(){/挂断逻辑/}};connection.setAddress(request.getAddress(),TelecomManager.PRESENTATION_ALLOWED);returnconnection;}}
-
通话录音实现(需特殊权限):
MediaRecorderrecorder=newMediaRecorder();recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);recorder.setOutputFile("/sdcard/call_record.3gp");recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);recorder.prepare();recorder.start();//需在通话建立后启动
关键问题解决方案:
-
双卡拨号选择:
使用PhoneAccount创建选择对话框:
TelecomManagertm=(TelecomManager)getSystemService(TELECOM_SERVICE);List<PhoneAccountHandle>accounts=tm.getCallCapablePhoneAccounts();Intentintent=newIntent(Intent.ACTION_CALL).setData(Uri.parse("tel:13800138000")).putExtra("android.telecom.extra.PHONE_ACCOUNT_HANDLE",accounts.get(0));
-
权限拒绝处理:
引导用户跳转设置页:
IntentsettingsIntent=newIntent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);settingsIntent.setData(Uri.parse("package:"+getPackageName()));startActivity(settingsIntent);
技术思考:
随着VoIP技术发展,传统通话功能正与互联网通信融合,建议开发者关注:
ConnectionServiceAPI实现跨平台通话控制
- WebRTC与原生通话的集成方案
- 5G网络下实时音视频通话的QoS保障
您在开发中是否遇到过双卡设备拨号选择异常的问题?或者有定制通话界面的需求?欢迎分享您的具体场景,我将为您提供针对性优化建议。