Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是经过审核的移动开发工程师。每周都会举行嘉宾分享,话题讨论等活动。

本期,我们邀请了 腾讯 SNG iOS 开发工程师“段定龙”,为大家分享《QQ电话适配iOS10 Callkit框架分享》。

分享内容简介:

苹果在iOS 10开放了系统电话权限,全新的Callkit框架能够让音视频的第三方应用获得系统级的通话体验,本次分享将主要介绍如何应用Callkit框架和一些适配经验。

下面是本期分享内容整理


大家好,我是来自腾讯SNG的段定龙,目前负责QQ音视频iOS客户端的开发工作,很高兴今天和大家分享一下QQ电话适配iOS10 Callkit的经验。

今天将从4个方面进行分享:

  1. Callkit概述
  2. Callkit框架
  3. 适配经验分享
  4. 更多资料

1. Callkit 概述

苹果在2016年的WWDC大会上推出了iOS10,提供了一系列更加开放的新特性,其中最吸引我们的就是Callkit,这个框架能够让第三方应用获得系统电话的权限以及体验。什么概念呢?上图吧。

这个框架解决了VoIP通话的三个痛点:

  1. 提高网络通话的音频权限:避免在通话过程中被传统电话无脑打断,更顺畅!
  2. 可以使用系统电话的UI界面:QQ电话真正地变成了“电话”!
  3. 可以使用系统服务,丰富了入口:比如锁屏的时候可以直接接听,通过系统通话沉淀发起和Siri唤起通话等

不得不给苹果点个赞,需求已宣讲,下面我们来看看怎么实现如此炫酷的体验。

2. Callkit 框架

2.1 整体结构

首先得介绍一下Callkit的框架。他分为三大模块:VoIP,CallCenter和来电屏蔽,要实现上述功能我们只需要关注Voip模块。Voip模块里主要有两个类:CXProvider和CXCallController。

CXProvider可以理解为处理系统电话界面有关的逻辑,比如来电呼起系统电话界面或者将用户在系统电话界面上的操作通知给App。 CXCallController则是将用户在App界面上的操作通知给系统。

2.2 四个主要流程的接口模块使用

更具体地,网络通话适配Callkit主要包含四个流程:收到来电主动通知Callkit、用户在Callkit界面点击接听、用户在手Q界面点击挂断、用户在系统通讯录发起新的通话。下面将通过四个流程来介绍CXProvider、CXCallController、INIntent事件的使用,举一反三。

首先我们看最简单的收到来电主动通知Callkit:

收到服务器信令通知后只需要调用CXProvider的reportNewIncomingCall就可以了。调用过后系统通讯录会自动沉淀,系统电话界面会展示。

图中的setCategory是为了避免出现无声问题,这个在后面会进行解释。

然后是用户在Callkit界面点击接听,这里的流程通用于用户对Callkit的操作回调:

用户点击接听后,我们会受到CXAnswerCallAction的回调,只需要在这里面添加App原来的音视频通话逻辑,再调用fulfill,整个流程就完成了。

再然后是用户在App内点击挂断

这时候我们需要添加一个CXEndCallAction到CXTransaction并调用requestTransaction请求执行,之后的流程与Callkit界面点击接听类似,收到CXEndCallAction回调,执行相应逻辑,调用fulfill完成流程。所有用户在app内的操作都以这种方式通知Callkit。

最后我们来看一下如何从App外部发起,以系统通讯录为例子(Siri其实是一样样的)

用户在点击系统通讯录的沉淀后,我们会收到系统事件通知(INStartAudioCallIntent或者INStartVideoCallIntent),然后就类似于用户在App内点击挂断的流程,只不过这次换成发起了,添加CXStartCallAction到CXTransaction并调用requestTransaction请求执行,收到CXStartCallAction的回调,执行相应逻辑后调用fulfill完成流程。

以上便是网络通话中主要的4个场景流程,不知道大家对CXProvider和CXCallController的功能和使用场景是否已经有一个大致的了解。最后用一张图来再解释一下:

适配过总的结构如图所示,系统界面由系统自己控制,我们没有办法直接对其进行操作,这里有点坑,有很多苹果的BUG无法避免,我们需要CXCallController去通知系统更新,并通过CXProvider的回调处理在系统界面上的操作。

回顾了一下整个Callkit的架构后,下面将分享一些适配时候的经验,包括ID的对应和无声问题的处理

3. 手Q适配框架及经验

3.1 适配手Q音视频架构

Callkit的架构里有两个ID标志,UUID和CXHandle,前者是用于表示每一次通话,后者则是用于标识具体的用户,比如reportNewIncomingCall的时候我们需要新的UUID去标识这次通话,而在系统通讯录沉淀的时候,则使用CXHandle区分用户。

QQ号码是一套独立的ID体系,区别于手机号,所以我们需要定义特殊的CXHandle字符串,并将UUID,CXHandle和QQ自己的AVID联系起来,统一管理。

3.2 无声问题的坑

整个适配过程中,我们遇到最大的问题就是出现通话无声问题,由于没有任何文档,在无数次的尝试后得出结论,苹果对于Callkit和App的音频接口调用顺序有严格的要求,如果不按照一下顺序来调用会出现无声问题甚至Crash:

稍微给大伙儿一点时间,看看这个图

图中不同颜色代表不同的流程,系统音频模块(AVAudioSession)分为六个操作:

  • 初始化(AudioUnitInit)
  • 去初始化(AudioUnitUninit)
  • 通知激活(didActivateSession)
  • 开启音频(AudioUnitStart)
  • 通知结束(didDeativateSession)
  • 关闭音频(AudioUnitStop)

重点其实就两个:

  1. 在流程开始前setCategory为PlayAndRecord
  2. 调用音频模块函数的时机:
    发起通话: Callkit回调 -> 初始化 -> fulfill -> 通知激活 -> 开启音频
    结束通话: Callkit回调 -> fulfill -> 通知结束 -> 关闭音频 -> 去初始化

4. 结语

最后提一下Pushkit通道的使用可以保证用户杀进程或者退后台了,依然可以后台唤起进程,完成通话,不过这不是今天的重点,就带过了。

由于苹果对整个架构真的没有什么文档解释,所有的工作都是在适配的过程中进行摸索,每个beta版本的接口都有所变动,太细节性的东西今天就不一一介绍了。

当然我们可以从一下途径获得更多资料(聊胜于无)

官方文档

https://developer.apple.com/reference/callkit

WWDC2016视频介绍

https://developer.apple.com/videos/play/wwdc2016/230/

WWDC2016演示文档

http://devstreaming.apple.com/videos/wwdc/2016/230b83wfxc7m69dm90q/230/230_enhancing_voip_apps_with_callkit.pdf

SpeakerBox: Using Callkit to create a VoIP app(9.13有更新的版本)

https://developer.apple.com/library/prerelease/content/samplecode/Speakerbox/Introduction/Intro.html

今天的分享讲到这里就结束拉,谢谢大家,如果有任何问题,欢迎大家提出来。

互动问答

Q1:什么是系统通讯录的沉淀是指在来电话后拒接,然后显示在通话记录里吗?

系统通讯录沉淀,就是比如打传统电话的时候,我们在电话app中最近通话里会有这次通话的记录,使用callkit后,所有未接,已接,呼出都会在最近通话中现实

Q2:uuid只是在通话中使用?如果发生变化有什么影响?

uuid只是用于每次通话过程成表示本次通话,相同用户的不同通话uuid是不同的,结束通话后这个uuid就没有意义了。

Q3:系统通讯录打电话不是用的系统电话,可以调起qq电话?

如果是由qq电话产生的通话记录,那么点击发起的时候会调用qq电话。

Q4:pushkit来唤醒app,有失败的可能吗?可靠性如何?

有失败的可能,比如我们后台向苹果后台发送,但是最终苹果后台没有给客户端下发,或者延时下发。目测还是比较可靠的,具体数据我这没有。成功率目测至少9成以上吧。

Q5:APP向下兼容到iOS7时,需要做些什么处理呢?

这个特性只在iOS10上适用,注意做好版本保护就行。

Q6:在系统通话记录中如果是 qq 电话,直接点击会发起qq 电话,这就是你说的 pushkit 嘛,唤醒程序,刚试了下,中间有次次失败了,还有就是对这次的通话 uuid,qq 的 id 这个是哪里得到的?

系统通话记录点击发起QQ电话并不是Pushkit, 而是Callkit提供的新特性。uuid是APP内生成的,qq的AVID取决于不同业务,也可以说是qq自己定义的。只是这是不同体系下的id需要做一些对应,通讯录发起时带的是cxhandle。至于bug。麻烦提供一下号码?后续跟进给你个答复

Q7:除了在不按照顺序来调用会出现无声问题甚至Crash,还有其它什么情况会导致该问题的发生?

主要注意设置一下avaudiosession的类型为playandrecord,不然也会导致无声

如果您觉得我们的内容还不错,就请转发到朋友圈,和小伙伴一起分享吧~


本文系腾讯Bugly独家内容,转载请在文章开头显眼处注明作者和出处“腾讯Bugly(http://bugly.qq.com)”

文章来源于腾讯云开发者社区,点击查看原文