语音通话模块介绍(三) CSipSimple介绍
CSipSimple简介
CSipSimple是基于PJSIP开源协议库实现的Android程序,这里主要围绕CSipSimple的功能实现讲解。凡是在CSipSimple中涉及到的Android知识点以及PJSIP相关内都会进一步的分析。
项目中主要有几个包:
api
包下主要是一些静态的常量,关于
sip
的管理。
db
包当然是数据库了,它也使用了标准的
ContentProvider
,其中一些建表的语句还是用
api
包中的类。
pjsip
就是这个库相关的类了,具体没有研究。在编译
jni
前,这部分是有问题的,因为它需要
jni
的调用,工程也会报错。编译后生成一些
jni
对应的调用文件就正常了,生成的文件在
org.pjsip
下。
utils
包就是一些帮助类,比如配置管理,音频自动聚焦。联系人,剪切版。
widgets
包是一些自定义的控件。
wizards
包是辅助用户的引导用的。
plugins
包是插件,没看出有什么用处。
service
包也是这里的重点:
MediaManager
音频管理的,控制不同的音频流,来电时的音频,蓝牙,静音等。
OutgoingCall
是呼出接收器,先通过这个拦截,可以拦截到系统电话的呼叫,与程序内的呼叫。然后自定义了选择器。
Downloader
处理下载的,更新的时候就是用这个下载的。默认使用的是
cacheDir
目录。
SipNotifications
是一个通知类,主要管理了通知。
SipService
这里的主要服务。
ui
包下就是关于界面的一些类了,
account
是添加帐户的。
filters
是关于帐户的过滤的,还使用了拖动排序的
ListView
。
calllog
就是通话日志了。日志有选择保存在系统的通话中还是软件中。
incall
就是来电的,与通话界面了。
dialpad
是拨号面板
还有一个包是
org.webrtc.videoengine
,这里是视频通话时用到的,一看名字就知道了
camera
。
暂时没有找到视频的插件的源码在哪里下载。但有编译好的
apk
。使用
x264
编码。
CSipSimple框架介绍
CSipSimple由应用层,JNI层,驱动层等组成。其中应用层和JNI层是该程序的核心,而驱动层根据设备提供。在应用层中,主要围绕了Android四大组件进行。其中:
Activity:完成界面UI显示;
Intent:主要是实现在Activity和Activity之间的切换
Service:完成activity和后台之间的联系以及进程间的通信
ContentProvider:完成数据保存及共享,主要保存一些设置,用户状态,好友等信息
Broadcast:主要完成在系统内信息的广播,一般会和ContentProvider结合,如当数据改变了,就携带信息发送广播,当所有已注册的广播接收器接收到内容之后进行解析之后进行相应的操作。
在JNI层,因为PJSIP核是基于C语言的,如果要用java调用,就必须封装本地的JNI库,从而实现从上到下的调用。
1 主界面
主界面是SipHome这个Activity。在该Activity中,引入了Andriod新版本才有的actionBar。ActionBar的作用可以统一页面导航和切换方式,可以突出显示一些重要的操作,而把一些不那么重要的或者使用频率低的放在overflow中。所以这里引入了4个Tab,分别对应DialerFragment,CallLogListFragment,FavListFragListFragment和ConversationFragment后通过使用ViewPager实现多页面切换就可以实现这几个activity的呈现。如图1.1:
2一些基本参数设置
在进行相关操作,如添加用户,打电话,发送信息之前。一些必要的设置如,只使用WIFI,开启ICE功能,开启STUN功能等参数设置必不可少。所以在刚开始使用时,一般我们会打开上面介绍的设置菜单项,其路径为:com.csipsimple.ui.prefs.hc.MainPrefs这个MainPrefs。 这里讲解MainPrefs这个Acitivty是如何实现的。首先该activity使用“headers”来进行屏幕显示。其界面如图2所示,共有6个header。而通过xml文件下进行相关的xml编写然后在MainPrefs中的onBuilder方法进行加载即可。而各个header都有相应的class。如图1.2:Andriod使用ContentProvider来保存数据。这里参数保存,文件配置在DBProvider.class和Preference中,其中DBProvider中主要和添加用户注册,信息,filter等关联。
3 添加用户(注册用户)
1
在
AccountsEditListFragment
中设置完相关参数信息后,跳转到
BasePrefsWizard
(
Intent
携带
wizardId
,我们这里使用的是
Basic
类型),可根据
wizardId
进行相应
wizard
的实例显示
2
当设置好注册用户的相关数据之后,点击保存按钮之后进入
BasePrefsWizard
下的
saveAccount
函数,在该函数中进行了
getContentResolver
().insert
语句,即关于操作
ContentProvider
的语句。这里根据
Uri
查询后进入
DBProvider
的
insert
方法,进而执行广播命令
getContext
().
sendBroadcast
(
publishIntent
).
3
Csipsimple
在这里注册了两个广播,一个
AccountWidgetProvider
和
DynamicReceiver4
,我们这里只用到
DynamicReceiver4
,其注册方式为动态注册(在
SipService
注册了
DynamicReceiver4
广播接收器 )
4
当上面第二点分析的发出广播信息后,主要有两个
reciever
会做出相应的操作,分别
DynamicReceiver4
和
AccountWidgetProvider
。
5
在
DynamicReceiver4
中重写了
onReceive
,
即也是接收信息的函数。然后进入判断语句,上面发送的广播所携带的信息为
ACTION_SIP_ACCOUNT_CHANGED
这样的
action
,
根据这个
action
,
执行了
SipService.setAccountRegistration
()
这个函数,从而跳转到
service
中执行。这里也就是用户开始注册的位置。
6
PJSUA
操作
:
在上面的添加用户操作中,其实已经和底层的
JNI
打交道了,如执行
SipService.setAccountRegistration
()
这个函数之后就会调用底层的
JNI
库。而在执行这些
JNI
库之前。又关于
PJSUA
的一些初始化设置。这一节主要说明这个。在说明进行初始化之前,先说明下
PJSIP
库框架
(
语音通话模块介绍(一))
7
通过
SipService
的
onStart
()
函数,加载
JNI
库
(
会跳到
PjSipService
的
tryToLoadStack
方法执行
) ,
而何时进行
PJSUA
初始化,
CSipSimple
中,在
SipService
开始,当注册完广播接收器之后还进行了这一步:
deviceStateReceiver.startMonitoring
()
,程序进入
DynamicReciever4
的
startMonitoring
()
函数,进而在
onConnectivityChanged
()
函数中调用了
SipService
的
restartSipStack
函数,从而进入
startSipStack
()
函数并调用了
pjService.sipStart
()
方法。而这个
sipStart
()
函数就是
PJSUA
初始化的所在处。
8 在
sipStart
函数中,结合
PJSIP
开发文档。从开发文档中我们知道,基础的
PJSUA-API
控制
PJSUA
的创建,初始化,启动,同时还提供各种辅助功能。在
sipStart
函数中,通过
status =
pjsua.create
();
完成
pjsua
的创建,除其他事项外,还初始化
PJLIB
,
PJLIB-UTIL
,并创建了一个
SIP endpoint
。在调用任何
PJLIB
功能之前这是至关重要的一步。
9 进行了上面的初始化之后,并进行一些
General Configure
。一般情况下,应用程序都将通常需要执行一些任务,使用
pjsua_transport_create
()
方法(该方法是
C
语言)来创建
SIP
传输。故在
CSipSimple
中有
createTransport
这样的方法来创建
SIP
传输。完成这些初始化之后,应用程序必须调用
status =
pjsua.start
()
开始
PJSUA
,此函数将检查所有的配置是否正确,如果没有则采用默认配置
10 在前面步骤中,用户添加是从接收器
DynamicReceiver4
执行了
SipService.setAccountRegistration
这个函数开始的,确切的讲,连接底层
JNI
库的都在
PjSipService
中进行,此处也是这样。进入到
PjSipService
中的
setAccountRegistration
方法。然后根据
account
的不同情况进行不同的操作,首次添加用户将会进行这一步
status =
pjsua.acc_add
(
account.cfg
,
pjsuaConstants.PJ_FALSE
,
accId
)
。
这里
acc_add
即调用了底层的
JNI
库的
pjsuaJNI.acc_add
,
从而返回添加成功与否的状态。根据该状态进行相应状态的改变,从而改变显示界面,这个是内容观察者
ContentObserver
的作用,下面这个步骤分析下这个内容观察器。
11 在
CSipSimple
中很多地方使用了
ContentObserver
进行更新。
ContentObserver
是个内容观察者。看过
HeadFirst
模式的话就知道,其实
ContentObserver
正是基于观察者模式实现的。所谓观察者,目的就是观察(捕捉)特定的
Uri
引起的数据库变化,当其所观察的
Uri
发生改变了,便进行触发。正是因为
ContentObserver
的实现是基于观察者模式的,所以其使用显得尤其简单,我们只需创建一个特定的
ContentObserver
派生类(如图),重载父类构造函数以及
onChang
()
方法处理回调后的功能实现(即观察者模式中某一事件发生变化之后进行
notify
),然后要进行相应的注册,即注册内容观察者和取消注册的步骤即基本完成操作
4 拨打电话
Java
层接口调用的是
placeCall
()
函数,进入
placeCall
函数之后,如判断为
SIP
用户,则进行
SipService
方法调用,否则直接进行基本用户拨打,如图程序
:
这里只说明使用
SIP
电话拨打情况。程序调用了
SipService
的
makeCallWithOptions
方法。进而进入
SipService
的
makeCallWithOptions
方法。如图所示:
这样,最终调用
PJsipService
的
makeCall
。如图所示: