linphone-android-客户端APP-工程解读
LinphoneLauncherActivity 是APP的入口组件,在这个组件里,它会启动LinphoneService这个后台服务,然后不断地判断这个后台服务是否已经启动完毕,如果已经启动完毕后,才让APP进入直接的功能组件LinphoneActivity。
LinphoneService是标准的android后台服务,这个后台服务非常的关键,它需要完成的工作包括以下的内容。加载APP需要使用的资源,启动LinphoneManager这个APP的全局管理器,LinphoneManager 这个全局管理器会一方面管理LinphoneService 的实例,另一方面管理LinphoneCore这个核心实例,同时生成目前需要处理的事件通知的LinphoneCoreListenerBase实例并注册到LinphoneCore这个核心实例的事件监听器中。目前的LinphoneCoreListenerBase 实例只监听callState,globalState,registrationState这三个事件。如果这个时候有电话呼入,那么callState事件触发,callState事件当中state会是LinphoneCall.State.IncomingReceived,APP需要切换到电话呼入的操作界面。registrationState这个事件反馈的是用户的SIP账号到SIP服务器的注册状态,是成功注册还是注册失败等。
LinphoneManager实现了LinphoneCoreListener, LinphoneChatMessage.LinphoneChatMessageListener这两个接口的部分方法,负责系统需要使用的资源的管理与初始化,管理着LinphoneCore的实例,负责了LinphoneCore的初始化和它的整个生命周期,同时又响应接收到文件消息与文件消息的响应,感觉它的整体职责是有点混乱的,可能作者开始是把它作为全局的统一资源管理器的,可是后来开发着开发着就什么东西都往里面放,就成了这样了。
LinphoneActivity是整个APP的核心界面组件,它采用了容器的结构模式来组织它所要包含的各个Fragment。它维护了一个专门用于存放各个Fragment实例的Fragment列表。它采用的布局文件是main.xml,针对手机与平板电脑分别做了一套布局文件,呵呵。这个布局文件主要是分为上下两个部分,然后上边部分主要是菜单与状态栏,下边那部分再分为两大个部分,一个部分是Fragment的容器,另一个部分是代表功能模块的TAB形式的工具栏。LinphoneActivity也会生成一个LinphoneCoreListenerBase的实例并注册到LinphoneCore这个核心实例的事件监听器中,它监听的事件是messageReceived(有用户给自己发信息,这个信息可能是文件也可能是文本),registrationState,callState。当有电话呼入的时候,这个时间当前的组件也会接收到这个事件,然后将界面切换到CallIncomingActivity这个界面组件。
CallIncomingActivity界面组件就是提供一个界面给用户选择接听还是挂断,并设置相关的接听参数。这个界面组件当中也生成一个LinphoneCoreListenerBase的实例并注册到LinphoneCore这个核心实例的事件监听器中,它监听的事件是callState,状态是State.CallEnd,这样在对方结束了电话呼入时好通知当前界面对方已经结束通话,不再呼入,那个APP应该结束当前界面,回到主界面状态。在CallIncomingActivity界面组件下,用户选择了接听电话,那么APP将切换到CallActivity这个界面组件下,同时判断当前用户是否接受视频电话,从而决定是视频通话,还是语音通话。
CallActivity这个界面组件是通话界面,这个界面组件当中也生成一个LinphoneCoreListenerBase的实例并注册到LinphoneCore这个核心实例的事件监听器中,它监听的事件是callState,callEncryptionChanged。callState会通知新的来电呼入,对方暂停通话,对方更新通话设置等相应的事件消息,发便当前界面针对这些消息作出相应的反馈。这个通话界面主要是控制两个Fragment的动态切换,一个是语音通话的CallAudioFragment,在这个Fragment里,用户可以启用不启用麦克风和摄像头等。同时还可以向新的用户发起电话呼叫,不过从目前的界面看是只允许当前用户向某一个用户进行通话的,如果再邀请一个用户进入通话的话,它是把上一个通话的用户设置为暂停通话状态,然后当前用户与多个用户通话的话,就是不断地切换当前通话的用户。另一个是视频通话的CallVideoFragment,这个Fragment主要是展现两个视频流的窗口,一个是当前用户自己的视频在界面的右下角,另一个是通话对方的视频。默认是展现CallVideoFragment,当用户点击CallVideoFragment这个界面的时候,又切换回CallAudioFragment,这样用户才能看到功能栏方便操作。
CallVideoFragment里有两个关键的SurfaceView,分别用户展现当前用户的视频和对方用户的视频。private SurfaceView mVideoView; 这个是对方用户的视频展现控件。private SurfaceView mCaptureView;这个是当前用户的视频展现控件。首先CallVideoFragment 会生成一个androidVideoWindowImpl的实例,这个实例再通过LinphoneManager.getLc().setVideoWindow(androidVideoWindowImpl)传入到linphone的核心当中,androidVideoWindowImpl的实例当中的相关事件监听器后续就能接收到内核的想着事件消息通知,从而将视频流传递到对应的SurfaceView当中,设置对方用户视频流到SurfaceView 当中的方法是LinphoneManager.getLc().setVideoWindow(vw),设置当前用户视频流到SurfaceView 当中的方法是LinphoneManager.getLc().setPreviewWindow(mCaptureView)。
DialerFragment界面组件,提供一个拨号的键盘,用来输入需要呼叫的SIP号码,然后进行呼叫。拨号呼叫调用的是LinphoneManager.getInstance().newOutgoingCall(mAddress)这个方法,传递的参数是对方的SIP号码或是SIP地址。newOutgoingCall这个方法里会先对当前使用的网络类型进行检测,用的是LinphoneUtils.isHighBandwidthConnection()方法。然后最终调用CallManager.inviteAddress()方法进行呼叫,这个方法最终又调用的LinphoneCore.inviteAddressWithParams(lAddress, params)方法进行呼叫。当LinphoneActivity 订阅的callState事件的state == State.OutgoingInit || state == State.OutgoingProgress的时候,APP界面组件切换到CallOutgoingActivity,由这个界面来展现正在呼出的状态。
CallOutgoingActivity这个界面组件,当中也生成一个LinphoneCoreListenerBase的实例并注册到LinphoneCore这个核心实例的事件监听器中,它监听的事件是callState,状态是State=Connected或是StreamsRunning的时候,说明对方接听成功,界面组件再切换回CallActivity,并显示是语音通话界面还是视频通话界面。
CallManager是专门用来进行电话呼叫的管理者类,专门负责对进行呼叫,或是对当前进行的呼叫进行参数设置的更新。
PreferencesListFragment和SettingsFragment界面组件是负责APP的参数设置,设置的参数非常的丰富,有账号,网络,音频,视频等。它们采用的是android框架自带的PreferenceScreen技术来建立整个参数设置的界面与功能,这样就省去了自己来做界面一个个实现相应的参数设置的功能。定义PreferenceScreen最关键的就是编写它对应的XML文件,这个XML文件编写好后,系统会根据它的内容来生成相应的设置界面,非常的方便,相应的技术点可以百度一下。一个PreferenceScreen里还可以嵌套多个PreferenceScreen,从而构建多层级的设置界面。系统里自带有多种类型的设置界面,如ListPreference,CheckBoxPreference等。但是这个PreferenceScreen它并不会保存相关的配置信息,所以这些配置参数的保存还是得由外部来进行保存,随后加载PreferenceScreen界面的时候,重新给里面的各个组件设置上相应的状态。目前在APP中是由LinphonePreferences这个类来负责了配置信息的保存的,而这个类又是依赖LpConfig这个具体负责读写配置文件的类来提供相应的功能,它真正的保存配置的文件是一个类似ini配置文件的文本文件,也就是讲linphone是自己实现的配置保存与读取功能,呵呵,没有用系统自带的,那我们也就沿用它的吧。默认的配置文件就在工程的res/raw下边,而PreferenceScreen配置文件就是在工程的res/xml下。
HistoryListFragment是呼入呼出历史列表的展现界面组件,这些呼入呼出的历史记录数据LinphoneCore是直接自己用数据记录下来了的,所以APP不需要考虑记录这些信息,除非需要自己定制地保存这些信息,不想用LinphoneCore的。通过LinphoneManager.getLc().getCallLogs()可以获得呼入呼出的历史记录。
LinphoneCore是整个linphone内核基于JNI的封装接口,LinphoneCoreImpl是linphone内核针对JAVA开发语言的封装。所有linphone内核的相关方法都是通过它来向android系统暴露。它里面有方法非常的多,这里需要关注几个关键的方法。
public void iterate();最为关键的主循环方法,应用程序应该经常调用它,因为它在后台会完成多件事情,如接收SIP指令,注册,认证,超时检测等,它必须要运行在LihpnoCore其它的方法所运行的线程里,说白了就是要保证LinphoneCore的方法运行在一至的线程里,不然就出错了。
public LinphoneCall invite(LinphoneAddress to)throws LinphoneCoreException;向对方发起一个电话呼叫请求。
public void terminateCall(LinphoneCall aCall);结束当前的电话呼叫。
public void declineCall(LinphoneCall call, Reason reason);直接挂断当前正在呼入的电话请求。
public LinphoneCall getCurrentCall();获取当前正在进行电话通话的实例。
public LinphoneAddress getRemoteAddress();获取当前通话下对方的SIP地址或是SIP电话号码。
public boolean isIncall();当前是不是处在通话状态下,这个通话要么在是进行的,也可能是暂停的,但没有挂断,还是连接着的。
public void acceptCall(LinphoneCall aCall) throws LinphoneCoreException;接收正在向自己电话呼入的通话。
public void acceptCallUpdate(LinphoneCall aCall, LinphoneCallParams params) throws LinphoneCoreException;接收通话的另一方对当前通话配置的修改,这样就能保证自己的通话配置与对方的一至。
public void deferCallUpdate(LinphoneCall aCall) throws LinphoneCoreException;拒绝通话的另一方对当前通话配置的修改,这样自己的通话配置就不会自动被修改了。
public LinphoneCallLog[] getCallLogs();获得通话日志列表。
public LinphoneCallLog getLastOutgoingCallLog();获得最近的通话日志列表。
public boolean isNetworkReachable();获取当前网络连接状态是否可连接。
void muteMic(boolean isMuted);禁用或是启用麦克风。
void enableAdaptiveRateControl(boolean enable);启用或禁用自适应速率控制,这个是根据网络带宽来自动调整传输速率的,小心慎用。
void enableEchoCancellation(boolean enable);启用或禁用语音的回声消除,这个是利用降噪的,echo limiter是回声门限。
void enableSpeaker(boolean value);启用或禁用扬声器。
void setVideoDevice(int id);设置某个摄像头为当前使用的摄像头。
int getVideoDevice();获取摄像头列表。
boolean isVideoSupported();检测客户是否支持视频通话
void enableVideo(boolean vcap_enabled, boolean display_enabled);启用或禁用视频,可以设置自己与对方视频的状态,但是如果通话已经建立正在通话中,那么调用这个方法是没有任何作用的,它必须在通话前调用。
int updateCall(LinphoneCall call, LinphoneCallParams params);修改已经建立的通话的状态,但是这个并不是能改通话的所有状态,针对视频在通话中的设置修改是无效的,如通话中不能修改视频的分辨率。
int getUploadBandwidth();获取最大上行网络带宽,单位是kbit/s.
void setUploadBandwidth(int bw);设置最大上行网络带宽,单位是kbit/s。
int getDownloadBandwidth();获取最大下行网络带宽,单位是kbit/s。
void setDownloadBandwidth(int bw);设置最大下行网络带宽,单位是kbit/s.
boolean pauseCall(LinphoneCall call);暂停当前的通话。
boolean resumeCall(LinphoneCall call);恢复当前的通话。
boolean pauseAllCalls();暂停全部当前的通话。
void terminateAllCalls();结束全部的通话。
LinphoneCall[] getCalls();获取当前的全部通话。
void transferCall(LinphoneCall call, String referTo);把当前与自己连接的通话转接到别的用户那里,自己的通话结束掉。
void transferCallToAnother(LinphoneCall callToTransfer, LinphoneCall destination);把当前的通话转接到另一个正在进行的通话当中。
void setPlayFile(String path);设置播放给对方听的音乐,通话还在呼入的状态下。