开源应用架构之asterisk

作者:Russell Bryant 翻译:jiazhengfeng

Asterisk[1]是一款GPLv2协议下的开源电话应用平台。简单来说,Asterisk是一个服务器应用,能够完成发起电话呼叫、接受电话呼叫、对电话呼叫进行定制处理。

Asterisk这个项目是由Mark Spencer于1999年开创的。Mark当时有一个名为Linux技术支持服务公司,公司需要一套电话系统来开展业务。由于Mark当时没有足够的钱购买,就决定自己研发一套。随着Asterisk逐渐流行,Linux支持服务公司逐渐将业务重点转移到Asterisk,并将公司的名字改为Digium。

Asterisk的命名是从Unix的通配符*来的,可以看出Asterisk的目标是能够做通讯里面的任何事情。为完成该目标,Asterisk现在已经支持了很多种能够发起和接受电话呼叫的技术,包括多种VoIP协议,与传统电话网络或者公用电话网络的模拟连接和数字连接。Asterisk的一个主要优势是能够让不同类型的电话呼叫可以呼入到Asterisk中,以及从Asterisk呼出到不同类型的电话。

电话呼入到asterisk或者从asterisk呼出,就具有了许多额外的特性,我们可以利用这些额外的特性来对电话进行定制处理。有些特性是经常用到的,诸如语音邮件。还有一些其他的特性可以与别的特性结合在一起用以创建和定制语音应用,诸如播放一个提示音,获取数字输入,或者语音识别等。

1.1 概念

本部分讨论对Asterisk各个部分都比较重要的结构概念,这些思想都是Asterisk体系结构的基础。

1.1.1 通道

    Asterisk中的通道,代表了Asterisk系统与一些电话终端之间的连接(图1.1)。最通常的例子就是电话呼叫到asterisk系统中。这个连接是由一个单侧的通道来表示的。在Asterisk代码里,通道是作为ast_channel这个数据结构的实例存在。这种单侧的呼叫场景比如一个主叫用户使用Asterisk中的语音邮件服务。

wps_clip_image-22165

                                                                            图1.1 单侧呼叫leg,用以表示一个单侧通道

1.1.2 通道桥接

一个更为熟悉一点的呼叫场景是两个电话间的连接。在这个场景里 ,有两个电话终端与Asterisk系统连接,所以这个通话里存在两个通道。

wps_clip_image-4152

图1.2 两个呼叫leg,代表了两个通道

当asterisk的通道像上面这样连接在一起的,就称之为一个通道桥接。执行通道桥接后将两个通道桥接在一起,其目的是在这两个通道间可以传递媒体信息。媒体流最常见的是音频流。当然,也可以在呼叫中包括视频流或者文本流。即便是包含多种媒体流,也是由单个通道来处理的。在图2中,有电话A和电话B对应的通道,桥接负责从电话A向电话B传递媒体和从电话B向电话A船体媒体。所有的媒体流都是通过asterisk来协商的。asterisk可以在不同的技术之间进行录音、音频操作、和转码。

两个通道桥接在一起,可以通过如下两个方法来完成:通用桥接和本地桥接。通用桥接是不管使用什么样的通道技术均能正常工作,这种桥接是通过asterisk的抽象通道接口来传递所有的音频和信令数据。这种桥接方法是最复杂的,也是最有效的。

本地桥接是和通话所使用的技术相关的一种桥接。如果两个通道使用相同的媒体传输技术,可以使用一种更高效的方式而不用通过像不同技术那种方式要通过asterisk的抽象层来完成。例如,如果连接到电话网络中硬件是固定的,可以通过在硬件上将两个通道桥接在一起,不用放到应用层来完成。有些VoIP协议,可以让终端之间直接互发媒体信息,只让信令信息通过服务器。

决定使用通用桥接还是本地桥接是在桥接的时候通过两个通道的比较完成的。如果两个通道均支持本地桥接,则采用本地桥接,反之,则使用通用桥接。为了判断两个通道是否支持相同的本地桥接方法,可以简单的通过c函数指针的比较。这种比较方法,并不是最优雅的方法,但是我们还没有遇到不能满足我们需要的情况。关于通道的本地桥接将会在1.2节讨论。图1.3说明的是一个本地桥接的例子。

wps_clip_image-3260

图3 本地桥接

1.1.3 帧

在asterisk代码里一个通话的通信是通过使用帧来完成的。帧是数据结构ast_frame的一个实例。帧可以是媒体帧也可以是信令帧。在一个基本的呼叫中,媒体帧流包括通过服务器的语音数据。信令帧用来发送与呼叫信令事件相关的消息,诸如,按下一个数字键,通话被保持,通话被挂断等。

    Asterisk中支持的帧类型列表是静态定义的,每种类型的帧是通过数字编码的类型(type)和子类型(subtype)标识的。完整的帧类型列表中include/asterisk/frame.h文件中,一些例子如下:

· VOICE: 这些帧携带部分语音流

· VIDEO: 这些帧携带部分视频流

· MODEM: 这种帧里面的数据的编码,诸如T.38是通过IP网络来发送传真的。这种帧类型主要是用来处理传真。一定要注意的一点是这种数据帧,一定要连续不能中断,以保证对端能对数据进行正确的解码。这与AUDIO帧不同,音频帧可以通过不同的音频编码进行转码虽然牺牲了音频质量但节省了网络带宽。

· CONTROL: 这种帧中包括的是通话的信令消息。这些帧通常用来说明通话信令时间,包括电话接通,挂断,保持等。

· DTMF_BEGIN: 数字开始处。这种帧一般是通话者在电话机上开始按一个DTMF按键。

· DTMF_END: 数字结束处。这种帧是通话者在结束电话机上的DTMF按键。

1.2 Asterisk组件抽象

Asterisk是一个高度模块化的应用程序。包括一个核心的应用,可以通过Asterisk代码树的main目录编译构建。但是,光这个核心通常没有什么用。核心应用主要处理模块注册,也有代码包括如何连接抽象接口来完成电话通话。具体的实现接口是通过可以在运行时刻加载到系统中的模块完成的。

默认情况下,所有的模块均放在asterisk预定义好的模块文件目录中,该目录下的所有模块会由主应用启动后进行加载。之所以这样设计,就是为了保持更加简单。在asterisk中还有一个配置文件,在这个配置文件中可以定义加载的模块和加载模块的顺序。这样会显得配置起来有点麻烦,不过可以让用户指定哪些模块中不需要时,可以不加载。这样最大的好处就是减少应用的内存占用,当然有时候也会有助于提高系统安全。最好的做法是如果不是非常需要,不要加载那些能够接受网络连接的模块。

当模块加载完成后,会向asterisk主应用注册本模块所实现的组件抽象接口。模块可以实现并向asterisk核心注册的接口有多种类型。一般而言,相关联的功能会放在一个模块中。

1.2.1 通道驱动

asterisk的通道驱动接口是最复杂也是最重要的可用接口。asteisk的通道API提供了对各种通信协议的抽象,使得asterisk的各种功能特性不必关心具体的通信协议。该组件主要是负责在asterisk通道抽象和具体的通信协议实现中的通信。

asterisk通道驱动接口的定义是ast_channel_tech接口。这个接口中定义了一些通道驱动必须要实现的方法。通道驱动首先要实现的方法是ast_channel工厂方法,即ast_channel_tech中的requester。当一个asterisk通道创建后,无论该通道是incoming方向的还是outgoing方向的,与该通道相关联的ast_channel_tech实现负责实例化和初始化该路通话对应的ast_channel。

ast_channel创建完成后,该结构中有一个创建该通道的ast_channel_tech指针。当然有很多其他的操作需要按照具体技术相关的方式来处理。图1.2中展示了asterisk中的两个通道,图1.4进行了扩展,展示了两个桥接的通道,以及通道技术如何实现的。

wps_clip_image-7189

               图1.4 通道技术和通道抽象层

在ast_channel_tech中最重要的方法包括:

· requester:用于向通道驱动请求并实例化一个ast_channel对象,根据通道类型进行适当的初始化工作。

· call: 用户向ast_channel表示的终端发起一个出局呼叫。

· answer: 当asterisk决定应该对ast_channel关联的入局呼叫进行应答时调用。

· hangup: 当系统决定当前的呼叫应该挂断时调用。通道驱动需要与终端按照一定的协议进行通信。

· indicate: 通话开始后,还会产生一些其他的事件,需要将这些事件通知给终端。例如,如果设备被保持住了,这个函数就会被调用。

· send_digit_begin: 当终端设备开始向asterisk发送按键DTMF的时候,调用该函数。

· send_digit_end: 当终端设备向asterisk发送按键DTMF结束的时候,调用该函数。

· read: 当asterisk核心需要从终端读入一个ast_frame数据帧的时候调用read函数。ast_frame帧是asterisk中用来封装媒体(诸如音频或者视频)和信号的抽象结构。

· write: 使用该函数向终端设备发送一个ast_frame帧。一般是由通道驱动来完成数据的处理(采集等)和打包使得数据包能够适合所采用的通信协议,然后将打包后的数据发送到终端。

· bridge:该通道类型中的本地桥接函数。前面提到了,进行本地桥接是通道驱动为相同类型的两个通道提供了一种更高效的桥接方法,而不是将所有的信令流和媒体流都通过额外的抽象层来完成。这对于提供性能极其重要。

通话结束后,asterisk核心中的抽象通道处理代码会调用ast_channel_tech中的hangup函数,然后销毁ast_channel对象。

1.2.2 拨号应用

asterisk管理员通过/etc/asterisk/extensions.conf中的拨号规划来设置呼叫路由。拨号方案是一系列的呼叫路由规则(称为extension)构成。电话呼叫进到系统中后,系统使用被叫号码在该呼叫应该使用的拨号方案中查找对应的extension。extension包括一系列可以在该通道上执行的拨号方案应用。拨号方案中使用的应用是由asterisk中的应用注册机制来维护的。应用的注册是在对应的模块加载的时候就完成。

asterisk提供了将近200个应用。应用的定义非常松散。应用可以使用asterisk的内部API与通道进行交互。有些应用只完成非常单一的工作,如playback应用,只负责给主叫播放一个声音文件。另外一些应用比较负责,执行多个操作,比如voicemail应用。

通过asterisk的拨号方案,多个应用可以一起使用来定制呼叫的处理过程。对于使用提供的拨号方案无法完成的复杂定制,可以使用脚本接口对呼叫进行个性化处理。脚本接口可以使用任意一种编程语言。在使用脚本的时候,拨号方案中的应用仍然可以与通道进行交互。

在我们进入例子之前,让我们一起看看asterisk拨号方案中处理呼叫1234这个号码的语法。注意,1234这个号码是随便选的。呼叫该号码后,调用了3个拨号方案应用,首先接听该通话,然后播放一个声音文件 ,最后挂断该通话。

; Define the rules for what happens when someone dials 1234.;exten = > 1234,1,Answer() same = > n,Playback(demo-congrats) same = > n,Hangup().csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

exten关键字是用来定义extension的。在exten行的右侧,1234是为呼叫1234这个号码定义的呼叫规则。下面的1是拨打1234这个号码时执行的第一步操作,Answer是告诉系统接听这个呼叫。下面的两行,是用same这个关键字开头的,是定义的上面的extension接下来的规则,这里就是1234接下来的规则。n是下一步要执行的操作,后面的项指明了拨号方案要执行的具体动作。

接下来是使用asterisk拨号方案的另一个例子。在这个例子中,进来的呼叫首先被接听,给主叫播放一个beep,然后从主叫读取4位的按键输入,并存储到DIGITS变量中,然后将读取到的数字播放给主叫,最后通话结束。

exten = > 5678,1,Answer() same = > n,Read(DIGITS,beep,4) same = > n,SayDigits(${DIGITS}) same = > n,Hangup().csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

前面提到过,应用的定义非常松散,注册函数定义非常简单:

int (*execute)(struct ast_channel *chan, const char *args);

应用实现的函数可以使用include/asterisk/.下所有的API函数。

1.2.3 拨号方案函数

很多拨号方案应用使用字符串作为参数。由于很多数值是硬编码的,因此在需要更多动态数值的时候,往往需要适时的使用变量。下面的例子中展示了拨号方案中如何设置变量,然后通过asterisk中的verbose应用在asterisk的命令行打印出变量的值。

exten => 1234,1,Set(MY_VARIABLE=foo)

    same => n,Verbose(MY_VARIABLE is ${MY_VARIABLE})

拨号方案的函数使用语法与前面提到的应用的语法一样。asterisk模块可以注册为拨号方案的函数,这些函数可以获取信息然后返回到拨号方案中,也可以接受一些信息并对信息进行处理。一般的原则是,拨号方案中的函数可以设置或者获取通道的原数据,但不能做信令或者媒体处理的事情,这些是由拨号方案中的应用来完成的。

下面的例子说明了拨号方案的函数用法。首先,在asterisk的命令行里打印出当前通道的主叫ID,然后通过Set函数修改主叫ID。

在这个例子中Verbose和Set都是应用,而CALLERID是函数。

exten => 1234,1,Verbose(The current CallerID is ${CALLERID(num)})

    same => n,Set(CALLERID(num)=<256>555-1212)

由于CallerID信息是存储在ast_channel数据结构中的,所以我们需要使用拨号方案函数而不仅仅是一个简单的变量。拨号方案函数代码知道如何从数据结构中设置和获取值。

另一个例子是使用拨号方案函数为呼叫日志(即呼叫详细记录CDR)中增加额外的信息。CDR函数可以获取呼叫详细信息,也可以添加定制的信息。

exten => 555,1,Verbose(Time this call started: ${CDR(start)})

    same => n,Set(CDR(mycustomfield)=snickerdoodle)

1.2.4 编码转换

在VOIP世界中,我们使用多种不同的编码来对媒体进行编码,并将编码后的数据发送到网络上。目前存在有很多的编码供选择,但在媒体质量、CPU消耗、带宽需求上会有所牺牲。Asterisk支持多种不同的压缩编码,并且在必要的时候可以进行编码格式间的转换。

在呼叫建立时,Asterisk会尝试让两个终端使用相同的媒体编码格式,这样就可以不用进行转码。但是,这只是理想情况。即便是有共同的编码,转码也仍然需要。例如,如果通过配置让asterisk对经过系统的音频数据进行信号处理诸如增加或者降低音量。Asterisk也可以通过配置进行通话录音,如果配置的录音文件的格式与通话的编码格式不一致,仍然需要编码。

说明:编码协商

协商媒体流使用何种编码的方法对于连接到asterisk所使用的各种技术是确定的。在有些时候,例如通过传统电话网络的呼叫,就不需要进行任何协商。但是,在另外一些情况下,特别是使用IP协议,需要使用一种协商机制,通过描述的终端的能力和优先选择的编码,协商出共同的编码。

以SIP协议为例,这里说明一下呼叫到asterisk后如何进行编码协商的。

1.终端向asterisk发起呼叫请求,在该请求中包含了终端希望采用的编码格式。

2.Asterisk通过查询管理员配置好的语音编码优先顺序,选择一种最优先的编码,该编码既是asterisk优先顺序表的编码,同时终端也可以支持。

Asterisk编码处理不太好的地方是对于较为复杂的编码,特别是视频编码。编码协商的需求在过去的10年内已经非常复杂。为了更好的处理新的音频编码和能更好的支持视频编码,我们还有很多工作需要做。这是asterisk下一发行版本中优先考虑的新需求。

编码转换模块提供了一个或者多个ast_translator接口的实现。编码转换器具有源和目的格式属性,提供了一个回调函数可以完成一段媒体信息从源格式到目的格式的转换。编码转换器本身对通话本身并不知道,所需知道的仅仅是如何完成媒体从一个格式到另一个格式的转换。

关于转换器API的更多信息,可以参考include/asterisk/translate.h和main/translate.c。转换器抽象的实现可以在codecs 目录中找到。

1.3 线程

Asterisk是一个典型的多线程应用程序,主要使用POSIX线程API来管理线程和相关的服务例如加锁。在Asterisk中所有和线程相关的代码为便于调试,都进行了一层封装。Asterisk中的大部分线程可以分为两大类网络监控线程和通道线程。通道线程有时候也指的是PBX线程,因为通道线程的主要目的是在一个通道上执行PBX。

1.3.1 网络监控线程

网络监控线程主要是在asterisk通道驱动模块中。他们主要负责监听驱动模块连接网络,监听来自网络的呼叫和其他类型的请求信息。监听线程会进行初始连接建立阶段的工作,诸如认证和被叫号码的验证。一旦呼叫建立完成,监听线程会创建一个asterisk通道,然后再创建的通道上开启一个通道线程来负责接下来的事情。

1.3.2 通道线程

正如前面讨论的,通道在asterisk中是一个基本概念,通道包含入局通道和出局通道。入局通道是呼叫进入到asterisk系统后创建的,拨号方案的执行就是在入局通道上进行的。对于每一个执行拨号方案的入局通道均会创建一个线程,称为通道线程。

拨号方案应用一般是在通道线程中执行。拨号方案函数基本上也是如此。拨号方案函数可以通过异步接口来进行读和写操作,比如Asterisk的命令行。但是,多数情况下是由ast_channel数据结构的所属通道线程来完成ast_channel生命周期。

1.4 呼叫场景

前面两部分介绍了asterisk组件的重要接口,和线程执行模型。本部分主要通过一些常见的呼叫场景来说明asterisk的组件间如何相互操作来完成呼叫处理电话呼叫的。

1.4.1 语音邮件检查

第一个呼叫场景是某人呼叫到电话系统中来查看自己的语音邮件。在这个场景中用到的第一个重要组件是通道驱动。通道驱动是负责处理从电话终端进来的呼叫请求,主要是在通道驱动的监控线程中完成的。取决于呼叫到系统中具体所使用的技术,可能会有某些协商来建立通话。建立通话的下一步就是决定呼叫目的号码,这一般是由主叫拨打的号码。但有些时候由于所使用的技术不支持被叫号码的传送,就不存在被叫号码。比如,从模拟线进来的呼叫就是这样。

如果通道驱动验证了asterisk拨号方案的配置文件中有被叫号码对应的extension,就会分配一个asterisk通道对象(ast_channel),并创建一个通道线程。通道线程将负担起呼叫剩下的主要任务,如图1.5所示。

wps_clip_image-7117

图1.5 呼叫建立顺序流程图

通道线程的大循环来负责拨号方案的执行,首先获取到被叫号码对应extension定义的规则,然后按照预定义好的顺序依次执行。下面是一个按照extensions.conf语法定义的例子。呼叫到*123时,首先接听该呼叫,然后执行VoicemailMain应用。
Exten => *123,1,Answer()
    same => n,VoicemailMain()

当通道线程执行Answer应用时,Asterisk将应答入局呼叫。接听呼叫需要与通道使用技术的处理,所以在通用应答处理之外,还要调用ast_channel_tech结构中定义的answer函数来处理接听通话,比如向IP网络发送专门的数据包,或者模拟线摘机等。

通道线程下一步是执行VoicemailMain,该应用是app_voicemail模块提供的。需要注意的一点是Voicemail代码虽然处理了很多和呼叫相关的交互,但应用本身对于呼叫到asterisk系统使用的何种技术并不知情,asterisk通道抽象将这些细节隐藏起来了。

用户通过呼叫访问语音邮件时,提供了很多特性。所有的特性主要是根据主叫输入的数字按键来读或者写音频文件。DTMF数字传送到asterisk中有很多种方式,这是由通道驱动来处理的。一旦某个键按下传送到asterisk中,就会转化成一个键按下事件,然后传递到voicemail代码中。

前面已经讨论过,在asterisk中一个重要的接口是编码转换。在这个场景中非常重要。当voicemail代码想播放一个声音文件给主叫时,音频文件中音频的格式和主叫与asterisk系统通信的音频格式未必一致。如果必须进行音频的转码,就会建立一个由一个或者多个编码转换器构成的转码路径来完成从源格式到目标格式的转换。

wps_clip_image-7691

图1.6 一个到VoicemailMain的呼叫

在某个时候,主叫会结束与语音邮件系统的交互,然后挂机。通道驱动将检测到这些动作,然后转换成asterisk通道信令事件。语音邮件代码会接收到该信令事件,然后退出。控制权将会返回到通道线程的大循环中继续拨号方案的执行。由于在这个例子里面没有接下来的拨号方案,通道驱动会优先处理通道相关的挂断处理,然后ast_channel对象会被销毁。

1.4.2 桥接呼叫

在asterisk中,另外一个比较常见的呼叫场景是在两个通道间桥接呼叫。这种场景下,一个电话呼叫通过asterisk系统。初始呼叫建立过程与前面的语音留言类似,不同之处在于从通话建立开始和通道线程执行拨号方案开始。

下面的拨号方案是一个建立桥接呼叫的简单例子。使用这个规则,当一个电话呼叫1234,拨号方案会执行Dial应用,Dial是发起一个出局呼叫的主要应用。
exten => 1234,1,Dial(SIP/bob)

Dial应用中的参数说明系统呼叫到的终端是SIP/bob。参数的SIP部分说明发起这个呼叫应该使用SIP协议。Bob是有通道驱动来负责解释使用的,实现是在sip协议chan_sip中。假设SIP通道驱动中事先定义好了一个帐号叫bob的,这个呼叫就会到达Bob的电话上。

Dial应用会向asterisk核心请求分配一个标识符为SIP/bob的asterisk通道。核心会请求sip通道驱动,完成技术相关的初始化工作。通道驱动会初始化外呼到终端电话的过程。通道驱动在呼叫进行过程中,会向asterisk核心传送一些事件,进而传送到Dial应用中。这些事件包括呼叫被应答,目标用户正忙,网络阻塞,呼叫被拒绝,以及其他的一些响应。理想的情况是,呼叫被应答。呼叫被接听会传回到入局通道中,asterisk不会对呼叫到asterisk系统中的入局呼叫进行接听除非出局呼叫被接听。两个通道均被接听后,通道间的桥接开始,如图1.7所示。

wps_clip_image-13067

图1.7 通用桥接中桥接通话的流程块

在一个通道桥接过程中,音频和信令事件从一个通道中传送到另一个通道,直到结束桥接的事件发生,例如一方挂断。图1.8的顺序流程图说明了桥接通话中一个音频帧上的关键操作。

wps_clip_image-269

图1.8 桥接中音频帧处理流程图

通话结束后,挂断过程与上面的例子类似,最大不同的地方是这里有两个通道。在通道线程结束之前,通道技术相关的挂断过程需要在两个通道上分别执行。

1.5 最后注释

Asterisk现在的体系结构已经超过10年了。但是通道的基本概念和asterisk拨号方案中灵活的呼叫处理仍然支持着复杂电话系统的继续发展。Asterisk没有很好的解决的一个领域是扩大到多个服务器。Asterisk开发社区目前正在开发一个项目叫Asterisk SCF主要就是为了解决伸缩相关的问题。在未来的几年里,我们希望看到Asterisk与Asterisk SCF一起继续在电信市场乃至大型安装上占据较大份额。

脚注 1. http://www.asterisk.org/2.DTMF代表双音-多频。电话通话中当有人按下电话按键时,发送的音频提示音。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434456.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 5.1 壁纸路径,RTFSC – Android5.1 壁纸设置流程简析 – RustFisher

Android5.1 壁纸设置流程浅析Ubuntu14.04 Android5.1 Source Insight3这里只是简单分析一下5.1里是如何设置壁纸的&#xff1b;这个流程和4.4有一些不同。但基本都是找个地方存放壁纸文件&#xff0c;需要的时候读取&#xff0c;设置的时候更新这里只看设置的过程。权当参考。…

android电梯程序设计,课内资源 - 基于Android实现的电梯调度模拟

一、使用说明1.1 项目简介某一楼有20层&#xff0c;操作者可以通过使用5部电梯从起始楼层来到达指定的楼层。本项目通过实现电梯调度&#xff0c;来模拟实现操作系统的调度过程&#xff0c;并且学习Android环境下使用Java的多线程编程方法以及调度算法。1.2 项目要求操作者可以…

asp.net 根据当前时间计算是否股票、期货、黄金交易日期

很简单的一个方法&#xff1a; 股票&#xff1a;每周1到周五&#xff0c;上午9:30到11:30 下午13:00 到15&#xff1a;00 期货&#xff1a;每周1到周五&#xff0c;上午9:00到11:30 下午13:30到15:00 黄金&#xff1a;每周1到周五&#xff0c;上午9:00到11:30 下午13:30到15:…

android mac 照片恢复,如何找回Android手机删除的照片?这方法你肯定不知道

安卓手机怎么恢复误删的照片?如何找回Android手机照片?现在大多数人都喜欢用手机拍照存照片&#xff0c;慢慢的&#xff0c;手机中的照片自然而然就会变多&#xff0c;而照片是最占内存的。当你照片达到一定数量&#xff0c;内存快满的时候&#xff0c;连照片都拍不了。所以就…

nubia android root权限,获取中兴NX403a (Nubia Z5S Mini Android 4.2)ROOT权限教程,新手必看...

中兴NX403a (Nubia Z5S Mini Android 4.2)如何获取ROOT权限?用什么ROOT工具最简单?请跟着小编一起来看看如何用奇兔刷机对中兴NX403a (Nubia Z5S Mini Android 4.2)进行一键ROOT&#xff0c;小编特别亲手制作了详细的中兴NX403a (Nubia Z5S Mini Android 4.2) ROOT教程图解&a…

A20 编译

android 源码编译分为 2 个部分&#xff0c;一部分是 linux 内核编译&#xff0c;这部分包括 uboot&#xff0c;另一部分是 android 源码编译。所以编译时各自单独编译&#xff0c;编译成功能&#xff0c;最近单独打包成最终烧录的镜像文件。2.1 源码结构说明首先把源码包 A20-…

apache OFBiz的安装

去官网下载OFBiz的代码包。解压这个代码包进入到命令行&#xff0c;输入以下命令 ant build-all 这时候会自动进行安装。这个过程大概有10多分钟。 运行startofbiz脚本。由于我是windows&#xff0c;所以运行startofbiz.bat。在网址输入http://localhost:8080/ecommerce/可以…

android 沉浸栏灰色,Android 沉浸栏实践——踩坑

当前开发环境&#xff1a;Android Studio 2.1.3&#xff0c;compileSdkVersion 24&#xff0c;buildToolsVersion "24.0.2"&#xff0c;support:appcompat-v7:24.2.0首先放个图&#xff0c;这就是我要做成的效果&#xff0c;Toolbar 和 Status Bar 一体共用背景图&am…

android实现文本输入,Android实现智能提示的文本输入框AutoCompleteTextView

今天我们要讲一个十分简单的内容&#xff0c;就是一个安卓控件的使用&#xff0c;用法很简单&#xff0c;但是很常用的一个。这里我用两种不同的写法来处理。当然&#xff0c;无论用哪一种写法&#xff0c;效果都是一样的。我们先来看效果图。要实现这种效果十分简单。需要一个…

ibatis的简介与初步搭建应用

一、ibatis的简介 ibatis是什么东西就不介绍了&#xff0c;自己去找谷老师。 这里讲下自己的使用体会。之前自己学过Hibernate&#xff0c;是看尚学堂的视频教学的&#xff0c;看完以后发现Hibernate体系中的内容真的很多&#xff0c;什么N-N关联、HSQL、缓存管理等等&#xff…

VOIP,PSTN,ISDN

近来在电视、杂志特集等展露头脚的「IP电话」&#xff0c;因其通话费用低廉而有口皆碑。IP电话利用的IP网络在互联网的世界里是众所周知的。互联网被认为基本上是免费的网络。接下来以此开放的技术和构想作为基础而登场的就是IP电话。IP电话与构成 所谓的IP电话 所谓的IP电话就…

svn在linux下的使用(svn命令行)ubuntu 删除 新增 添加 提交 状态查询 恢复

合并步骤&#xff1a;&#xff08;1&#xff09;先切换到分支&#xff1b;&#xff08;2&#xff09;svn merge trunk . &#xff08;3&#xff09;svn sw trunk &#xff08;4&#xff09;svn merge --reintegrate branch . svn merge http://59.251.189.152:8989/svn/ron/tru…

计算机组装与维护模拟测试题三答案,春季高考信息技术模拟题3(计算机组装与维修部分含答案)...

36.计算机硬件系统的核心部件是A&#xff0e;主板B&#xff0e;CPUC&#xff0e;内存D&#xff0e;硬盘 37.北桥芯片的功能不包括A&#xff0e;对CPU类型和主频的支持B&#xff0e;对ECC纠错的支持C&#xff0e;对内存类型和最大容量的支持D&#xff0e;对USB接口的支持 38.操作…

wince6下usb摄像头(UVC)使用指南

转自&#xff1a;http://hi.baidu.com/cahbb/blog/item/6d76093498fe0790a61e120e.html 在搭好了wince6的开发环境之后&#xff0c;下一步试验一下wince6下使用摄像头的方法。搜集了不少资料&#xff0c;从中学习了很多有用的东西&#xff0c;在这给记录下来&#xff0c;方便…

使用SqlCommandBuilder

使用命令构造器添加行 View Code using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient;namespace PersistAddsBuilder {class Program{static void Main(string[] args){string connStri…

Asterisk权威指南/第二章 Asterisk架构

Asterisk和其他传统的PBX完全不同&#xff0c;因为Asterisk的拨号计划以同样的方式处理所有的入局信道&#xff08;incoming channels&#xff09;。 传统的PBX在逻辑上区分工作站信道&#xff08;连接电话机&#xff09;和电话局信道&#xff08;连接到外部世界&#xff09;。…

x-lite asterisk 成功实现视频通话

首先&#xff0c;在此感谢asterisk协会的各位大牛们&#xff0c;没有他们的帮助&#xff0c;我也不可能在这么短的时间内实现&#xff0c;x-liteasterisk音视频通话。在此将实现的过程记录如下&#xff0c;分享给asterisk的爱好者们。 1. 修改asterisk服务器的sip.conf文件&…

axure 链接html文件,通过WuliHub免费托管原型Axure HTML文件

产品经理常用的工具就是Axure&#xff0c;通过Axure把想法转化成包含交互的原型线框图。在工作中&#xff0c;我们用HTML文件传递不仅会遇到某些浏览器无法打开的问题&#xff0c;而且没有办法及时更新内容。所以&#xff0c;介绍一种简单的并且免费的方式来方便管理HTML原型&a…

Asterisk SIP连通测试(X-Lite eyebeam)

Step1:设置 sip.conf rootUbuntu:/etc/asterisk# vim sip.conf [general] //类似与全局变量 context default srvlookup yes //DNS SRV记录查询 [111] secretaaa //密码&#xf…

html多出的空白页怎么删除,word多出一页空白页怎么删除,这4个方法总有一个能解决,真实挂机网赚项目...

信赖大多数人都碰到过这样的难题&#xff0c;在编辑Word文档的时刻&#xff0c;是不是在中心或者是最后一页&#xff0c;莫名其妙的泛起空白页&#xff0c;而且这个空白页怎么删都删不掉。不要着急&#xff0c;今天就给人人分享4种简朴又好用的解决方式&#xff0c;总有一种能让…