天天看点

CSipSimple程序之基本功能(三)

4 PJSUA操作

在上面的添加用户操作中,其实已经和底层的JNI打交道了,如执行SipService.setAccountRegistration()这个函数之后就会调用底层的JNI库。而在执行这些JNI库之前。又关于PJSUA的一些初始化设置。这一节主要说明这个。

在说明进行初始化之前,先说明下PJSIP库框架,其框架如图12所示:

CSipSimple程序之基本功能(三)

图12 PJSIP框架

图12展示了PJSIP框架的各模块,可以看出从上到下,Application(pjsua)模块可调用下层所有的模块,也即是PJSUA处于最高层,其整合了下层模块的全部功能,所以这也就是为什么我们基本的操作都在PJSUA这里进行。是因为通过PJSUA,我们就能很方便的深入到其他模块中。接着Application模块往下就是PJSUA_lib层,要让应用层(PJSUA)能更好的调用,当然得有个封装好的库,这个库就是PJSUA_LIB库,称为高层用户代理库,集合SIP,Media以及NAT穿越,所以也就有了往下的PJMEDIA-CODEC和PJMEDIA(负责SDP协商媒体编码和媒体传输),PJNATH(解决NAT穿越),PJSUA-UA(提供SIP用户代理库),PJSIP-SIMPLE(实现presence和及时消息),PJSIP(核协议栈,SIP协议),PJLIB-UTIL(提供有用的工具函数)以及PJLIB(每个功能根据其所在的层次以及负责的功能提供丰富的接口)等模块。

知道了PJSIP的基本框架之后,我们回到CSipSimple,在SIP_HOME UI中的OnResume函数下会进行SipService开启。即通过如图13语句之后进入SipService。

// Service monitoring stuff
    private void startSipService() {
        Thread t = new Thread("StartSip") {
            public void run() {
                Intent serviceIntent = new Intent(SipManager.INTENT_SIP_SERVICE); 
                serviceIntent.putExtra(SipManager.EXTRA_OUTGOING_ACTIVITY, new ComponentName(SipHome.this, SipHome.class));
                startService(serviceIntent); //启动了SipService
                postStartSipService();
            };
        };
        t.start();

    }
           

图13 通过Intent启动SipService

         Service的生命周期因不同的启动方式会有所不同,这里是通过context.startService启动的。所以其生命周期如下:context.startService() ->onCreate()- >onStart()->Service running-->context.stopService()->onDestroy() ->Service stop。通过onStart()函数,加载JNI库(会跳到PjSipService的tryToLoadStack方法执行),如图14所示:

public boolean tryToLoadStack() {
        if (hasSipStack) {
            return true;
        }

        // File stackFile = NativeLibManager.getStackLibFile(service);
        if (!sipStackIsCorrupted) {
            try {
                // Try to load the stack
                // System.load(NativeLibManager.getBundledStackLibFile(service,
                // "libcrypto.so").getAbsolutePath());
                // System.load(NativeLibManager.getBundledStackLibFile(service,
                // "libssl.so").getAbsolutePath());
                // System.loadLibrary("crypto");
                // System.loadLibrary("ssl");
                System.loadLibrary(NativeLibManager.STD_LIB_NAME); //加载jni本地库
                System.loadLibrary(NativeLibManager.STACK_NAME);
                hasSipStack = true;
                return true;
            } catch (UnsatisfiedLinkError e) {
                ...
            } catch (Exception e) {
               ...
            }
        }
        return false;
    }
           

图14 PjSipService类加载JNI库

         而何时进行PJSUA初始化,CSipSimple中,在SipService开始,通过相关的调用完成的。从图7可看到,当注册完广播接收器之后还进行了这一步:deviceStateReceiver.startMonitoring()。这是程序进入DynamicReciever4的startMonitoring()函数,进而在onConnectivityChanged()函数中调用了SipService的restartSipStack函数,从而进入startSipStack()函数并调用了pjService.sipStart()方法。而这个sipStart()函数就是PJSUA初始化的所在处。

         我们进入sipStart函数,结合PJSIP开发文档。从开发文档中我们知道,基础的PJSUA-API控制PJSUA的创建,初始化,启动,同时还提供各种辅助功能。在sipStart函数中,通过status = pjsua.create();完成pjsua的创建,除其他事项外,还初始化PJLIB,PJLIB-UTIL,并创建了一个SIP endpoint。在调用任何PJLIB功能之前这是至关重要的一步。

         进行了上面的初始化之后,并进行一些General Configure。一般情况下,应用程序都将通常需要执行一些任务,使用pjsua_transport_create()方法(该方法是C语言)来创建SIP传输。故在CSipSimple中有createTransport这样的方法来创建SIP传输。完成这些初始化之后,应用程序必须调用status = pjsua.start()开始PJSUA,此函数将检查所有的配置是否正确,如果没有则采用默认配置。

         经过上面的初始化之后,我们就可以进一步解释2.3节的添加用户操作中,PJSUA是如何操作的。

         在2.3节中已经说过,用户添加是从接收器DynamicReceiver4执行了SipService.setAccountRegistration这个函数开始的,确切的讲,连接底层JNI库的都在PjSipService中进行,此处也是这样。进入到PjSipService中的setAccountRegistration方法。然后根据account的不同情况进行不同的操作,首次添加用户将会进行这一步status = pjsua.acc_add(account.cfg, pjsuaConstants.PJ_FALSE, accId)。这里acc_add即调用了底层的JNI库的pjsuaJNI.acc_add,从而返回添加成功与否的状态。根据该状态进行相应状态的改变,从而改变显示界面,这也是2.3节中最后一点讲到的内容观察者ContentObserver的作用。