利用CActiveSchedulerWait 将异步操作转换为同步

来源:互联网 发布:淘宝二手手机可以买吗 编辑:程序博客网 时间:2024/05/29 14:10
 

最近看Liuxg的IMEI示例,终于把CActiveSchedulerWait稍微搞明白了一点,赶快记录下来,否则过一阵又要忘了。

总的来说,CActiveSchedulerWait应该用在某个活动对象中,目的是把某个异步操作转换为同步操作。以获取IMEI为例:
class CImeiEngine : public CActive
CImeiEngine是一个活动对象,获取IMEI的核心操作是一个异步操作,看如下代码:
void CImeiEngine::GetImei()
{
if ( IsActive() )
{
Cancel();
}

CTelephony::TPhoneIdV1Pckg phoneIdPckg( iPhoneId );
iTelephony->GetPhoneId( iStatus, phoneIdPckg );

SetActive();
StartWait();
}
我们就是通过CTelephony::GetPhoneId来获取IMEI号的,但是我们在实际使用中,最好将CImeiEngine::GetImei方法做成同步的,这样最方便。设想一下调用流程:
iImeiEngine->GetImei();//iImeiEngine是一个CImeiEngine实例
DoSomeThingWithImei();//此时IMEI号肯定已经获取成功

为了保证以上的同步调用正常工作,GetImei()必须是一个同步方法,这就是需要使用CActiveSchedulerWait的原因。
看一下CImeiEngine::StartWait的实现:
void CImeiEngine::StartWait( )
{
if ( iWait.IsStarted() != (TInt)ETrue )
{
iWait.Start();
}
}
代码相当简单,就是调用了CActiveSchedulerWait::Start方法而已。
程序运行到CImeiEngine::GetImei中的StartWait()这行代码后,将会一直阻塞(实际上是阻塞在iWait.Start()这一行),直到CImeiEngine::RunL被调用:
void CImeiEngine::RunL()
{
iWait.AsyncStop();

if ( iStatus == KErrNone )
{
// The request is successful
TBuf manufacturer = iPhoneId.iManufacturer;
TBuf model = iPhoneId.iModel;
TBuf serialNumber = iPhoneId.iSerialNumber;

if ( iObserver )
{
CTelephony::TPhoneIdV1Pckg phoneIdPckg( iPhoneId );
iObserver->UpdatePhoneInfo( serialNumber );
}
}
}
在RunL中,首先调用iWait.AsyncStop()这会导致CActiveSchedulerWait结束等待,也就是回到iWait.Start()之后。但是因为活动对象的特殊性,走完iWait.AsyncStop()这步并不会立刻返回到iWait.Start(),而是在RunL走完才会返回,所以没有必要将iWait.AsyncStop()放到RunL函数的末尾。
这样,CImeiEngine::GetImei就被设计成为一个同步方法了。

将异步转换为同步,还可以使用User::WaitForRequest方法,但是据说有些情况下无法正常工作,这个方法会一直挂起,我倒是没有机会遇到这种情况。

题外话:
为了获取IMEI号,也可以不将GetImei设计为同步方法,这样就避免了使用CActiveSchedulerWait。这样做需要尽可能早地执行CTelephoy::GetPhoneId,并把获取到的IMEI号保存起来,在使用IMEI号前还需要判断是否已经成功获取。无论如何,这种方案都没有同步获取来得安全。

附注:
以上示例中,iWait是CImeiEngine的一个成员对象:
class CImeiEngine : public CActive
{

private:
CActiveSchedulerWait iWait;

}
iWait作为CImeiEngine的一个成员变量,在CImeiEngine初始化完成之后就已经被构造,所以无须手工构造及释放。