IOS教程之编写平安的Symbian C++游戏代码仓酷云
由于封闭文档写的相当好在遇到新框架的时候弄明白框架的功能去文档里搜搜框架的ProgrammingGuide很有用要弄明白框架类的继承结构写iOS的程序不一定都是用OBJC本文献给利用NokiaSymbian60SDK各个版本开辟游戏软件的程序员。固然本文次要是针对游戏软件,可是年夜部份内容对一样平常使用软件也一样合用。1.1.声明
为了不本心的斥责,起首我必需供认一点,我自己并非靠SymbianC++生活。除forum.nokia.com上的文章和SDK,我也没有看过任何干于Symbian的书本。只是偶尔的,我在天津猛犸游戏公司(www.mammothworld.com)熟悉并打仗了Symbian。我从零起步,写出了一个糟糕的Symbian游戏引擎并在3650、7650上开辟了一些游戏。以是我对Symbian的把握完整是出于本人的推测和了解,固然本文缺少威望,但最少都是履历之谈,简单了解。
1.2.概述
Symbian游戏是运转在手机上的游戏,它不克不及搅扰手机一般的通信功效,对操纵体系和别的使用程序必需和睦。而在初次编写SymbianC++游戏时,我碰到了有数奇异的成绩,个中年夜部分成绩出在内存极度不敷,翻开太多使用程序,屏幕回护探出,接到短信、德律风等特别情形下。
实在假如养成松散的代码作风,举行充足的毛病处置,年夜部分成绩本能够制止。为懂得决它们,我非常花了一番工夫,以是在此把我的一些教导、履历写出,但愿人人能制止犯一样的毛病。
假如你不是专业喜好者,而是为一个仔细的开辟商事情,出格是假如你的产物必要经由过程SymbianSigned认证(www.symbiansigned.com),你就必需加倍当心的看待本文提出的成绩。
SymbianSigned是一个针对Symbian使用程序的认证,想要经由过程它,你的使用程序必需经由过程一系列严厉的测试。认证对使用程序的文件办理、内存利用、体系事务呼应、收集、资费和公家数据等都有必定的请求。假如想懂得SymbianSigned认证的具体内容,能够往它们的网站下载白皮书。
1.3.非常处置
固然我们都晓得任何一个new(ELeave)大概带有L后缀的函数都大概抛出非常,可是良多专业的喜好者仍是会无视SymbianC++中非常处置的主要性。固然有些函数只要在极为稀有的情形下才会抛出非常。但不是耸人听闻,假如你不写代码捕获并处置它们,使用程序就会碰到"体系毛病"。
一般C++利用throw抛出非常。非常抛出后,栈会一直回滚,直到碰到比来一层catch为止。SymbianC++中的非常处置不利用try-catch和throw。可是它的处置机制和尺度C++非常相似,区分仅仅是它只能抛出一个整数毛病码,而不是一个恣意对象。
我将从非常的抛出、捕获、处置三方面解说这部份内容。
1.3.1.抛出非常
SymbianC++中,有上面几种情形下会抛出非常:
利用静态函数User::Leave抛出非常。这个函数就是最基础的非常发生函数。上面讲的别的抛出体例都能够转化为User::Leave。
利用静态函数User::LeaveIfError把毛病码转化为非常。有些函数好比CFbsBitmap::Create()有一个TInt的前往值。假如碰到毛病,这些函数就会前往非KErrNone的毛病值。此时,可使用LeaveIfError把这个前往值转化为非常。好比:User::LeaveIfError(bmp->Create(iSize,EColor4k);实在LeaveIfError就是if(returnValue!=KErrNone)User::Leave(returnValue);
利用new(ELeave)请求内存。假如没有充足内存可用,此操纵发生一个KErrNoMemory非常。好比TText8*p=new(ELeave)TText8;相称于TText8*p=newTText8;if(p==NULL)User::Leave(KErrNoMemory);
挪用带有L后缀的函数。Symbian体系的定名标准中请求,每个大概Leave的函数都要有后缀L。包括有带L的内层函数挪用的外层函数也必需加上L。这类函数中最多见的就是NewL,NewLC和ConstructL。这个标准比你设想的要主要。由于它给其他程序员一个表示,提醒他们对这些函数举行回护。
1.3.2.捕获非常
相似尺度C++的catch语句,SymbianC++的TRAP关头字能够对一个大概发生非常的函数举行回护,而且捕捉到非常值。好比:
TInterrorCode;
TRAP(errorCode,SomeDangerousFuncL());//回护实行SomeDangerousFuncL()函数
if(errorCode!=KErrNone)
{
//捕获到了一个非常,在这里增加处置非常的代码
}
相似的TRAPD省往了你声明一个部分变量的贫苦。头两行代码能够简写成:
TRAPD(errorCode,SomeDangerousFuncL());
1.3.3.处置非常
关于分歧的非常固然有分歧的处置办法(空话:-))。我们以最多见的捕捉到代表内存不敷的KErrNoMemory非常为例解说。
注重在Container,AppUi等类的机关过程当中,你不必要到场对内存不敷的回护。由于这统统体系已为你做好了。体系会弹出一个对话框呈报内存不敷。依据你的操纵体系版本分歧,这多是中文的,也多是英文大概别的言语的。假如你不信,能够在AppUi大概Container的ConstructL中写一行User::Leave(KErrNoMemory)碰运气。我实验的了局以下:
除下面说的特别情形,你能够复杂的弹出一个对话框,告知用户没有充足的内存运转程序,而且平安的封闭程序。好比我的游戏程序就是如许处置的:
voidCStageManager::DoGameFrame()
{
TRAPD(error,DoGameFrameProtectedL());
if(error==KErrNoMemory)
{
StopGame();
m_noMemoryDlg->ExecuteLD(R_KEY_INVALID_DIALOG);
Exit();//CallCAknAppUi::RunAppShutter()
}
elseif(error!=KErrNone)
{
User::Panic(_L("Someothererror."),error);
}
}
个中noMemoryDlg是间接大概直接在Container的ConstructL中创立的:
//inheaderfile
CAknQueryDialog*m_noMemoryDlg;
//somewhereinConstructL
TBuf<128>errMsg;
_LIT(formater,"Notenoughmemory.Pleaseclosesomeapplications.");
errMsg.Copy(formater);
m_noMemoryDlg=new(ELeave)CAknQueryDialog(errMsg,CAknQueryDialog::EErrorTone);
固然,你也能够制造一个优美的图片来呈报内存不敷,守候用户按恣意键再加入。不外载进这个图片也大概会失利,以是最少在这个图片乐成载进之前,你仍是必要体系对话框来呈报的。
值得一提的是,你纷歧定必要加入程序,大概你能够稍后重试请求内存,侥幸的话,没准第二次就可以乐成。这是由于Symbian体系会在内存不敷时主动封闭一些使用程序。我以为这是Symbian体系一个对照奇异的计划。一般使用程序在AppUi的HandleCommandL中会呼应EEikCmdExit动静,而且挪用CAknAppUi::Exit()函数(以下代码)。这使得使用程序能够在使用程序办理器顶用C键停止失落。这也使得Symbian操纵体系无机会在内存不敷时经由过程这个渠道主动封闭一些使用程序。
//----------------------------------------------------
//CFlyAppUi::HandleCommandL(TIntaCommand)
//takescareofcommandhandling
//----------------------------------------------------
//
voidCFlyAppUi::HandleCommandL(TIntaCommand)
{
switch(aCommand)
{
caseEEikCmdExit:
Exit();
break;
//TODO:AddYourcommandhandlingcodehere
default:
break;
}
}
坦率说我没有实验太重试请求内存这个举措,不外我想是可行的。
1.3.4.栈回滚和对象的平安析构
下面说到在碰到某些非常时,你能够选择弹出对话框而且停止程序,实在这会比你设想的要坚苦一些。由于C++可不像Java那样有托管堆举行渣滓搜集。不外幸亏C++栈会主动回滚,栈上的对象会被烧毁。假如你此时挪用CAknAppUi::RunAppShutter()停止程序,那末AppUi,Container的析构函数会顺次被挪用,引发你本人创立对象的析构函数也顺次被挪用。那末堆上的对象也要被烧毁。但是,请记着,非常随时到处大概产生,使对象处于一种"半机关"的形态。此时析构函数被挪用大概会形成对有效指针的会见毛病。请看上面这个例子,它犯了两个罕见的毛病:
classBadExample
{
protected:
TText8*m_pBuf;
TText8*m_pBuf2;
public:
staticBadExample*NewL()
{
BadExample*self=new(ELeave)BadExample();
self->ConstructL();
returnself;
}
voidDeleteBuf()
{
deletem_pBuf;
}
voidRebuildBufL()
{
m_pBuf=new(ELeave)TText8;
}
private:
BadExample();
~BadExample()
{
deletem_pBuf;
deletem_pBuf2;
}
voidConstructL()
{
m_pBuf=new(ELeave)TText8;
m_pBuf2=new(ELeave)TText8;
}
};
假定我们在AppUi的ConstructL中利用BadExample::NewL()来机关对象,在AppUi的析构函数中delete这个对象。
上面我们剖析一下大概碰到的成绩:
起首,在函数NewL中,self指针没有被回护,试想假如self->ConstructL()一句抛出非常。那末这个self指针指向的对象就没有return给外界(也就是AppUi),这个对象就永久"丧失了",形成了内存保守。准确的做法是利用CleanupStack对它举行回护。CleanupStack最少能包管在程序加入时压进个中的对象都能烧毁。
staticBadExample*NewL()
{
BadExample*self=new(ELeave)BadExample();
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
returnself;
}
可是注重,此处另有一个奇妙的内存保守。细心看看CleanupStack::PushL()的声明:
IMPORT_CstaticvoidPushL(TAny*aPtr);
IMPORT_CstaticvoidPushL(CBase*aPtr);
IMPORT_CstaticvoidPushL(TCleanupItemanItem);
假如传进的指针是CBase指针,那末CBase的虚析构函数(virtual~CBase())就可以包管对象在烧毁时准确的挪用析构函数。但是本例中BadExample不是从CBase中派生,那末对象只能做很无限的烧毁,基本不会挪用析构函数。以是,假如ConstructL是因为第二个内存请求m_pBuf2失利,那末m_pBuf请求的内存就永久不会接纳。以是准确的做法是,让BadExample从CBase派生。
classBadExample:publicCbase
其次,我们并没无为m_pBuf和m_pBuf2赋初值,在Release版中他们的值是随机的。那末,假如m_pBuf2的请求失利,析构函数仍是会实行deletem_pBuf2,试图删除一个有效指针。准确的做法是在机关函数中为m_pBuf和m_pBuf2赋初值NULL。由于尺度C++划定,delete一个空指针不做任何操纵。不外实践上,假如对象从CBase派生,这一步是没有需要的,由于CBase能包管派生类的成员变量在机关时主动清零。
最初,静态的利用DeleteBuf和RebuildBufL是不平安的。假如你先用DeleteBuf删除这个对象,那末m_pBuf就是一个坏指针。但是紧接着的RebuildBufL大概会失利。此时假如析构函数被挪用,仍是会发生delete有效指针的毛病。准确的做法是,在DeleteBuf中,把m_pBuf设为NULL。
总结下面说到的几点,完全的平安的代码是:
classBadExample:publicCBase
{
protected:
TText8*m_pBuf;
TText8*m_pBuf2;
public:
staticBadExample*NewL()
{
BadExample*self=new(ELeave)BadExample();
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
returnself;
}
voidDeleteBuf()
{
deletem_pBuf;
m_pBuf=NULL;
}
voidRebuildBufL()
{
m_pBuf=new(ELeave)TText8;
}
private:
~BadExample()
{
deletem_pBuf;
deletem_pBuf2;
}
voidConstructL()
{
m_pBuf=new(ELeave)TText8;
m_pBuf2=new(ELeave)TText8;
}
};
1.4.平安的图象引擎
SymbianC++游戏的2D图象显现部分一样平常由上面几个类构成:
图象-封装了一个CWsBitmap。是基础的图片资本。撑持图象之间的各类贴图和夹杂操纵。
双缓冲-一个和屏幕分辩率、色深相称的图象。
间接写屏撑持-复合一个CDirectScreenAccess对象,完成MDirectScreenAccess接口。卖力间接写屏的平安处置。好比来电、屏保时合时的中断和开启间接写屏与游戏逻辑。
画图类-卖力在图象中画图。它不是对Gc的封装,而是经由过程间接修正图象内存区举行画图。
位图字体类-利用事后创立的位图资本写字。以下图就是一个事后创立的位图资本。长处是速率快,弱点是没法撑持年夜字符汇合,好比中文。
字体缓冲区类-仍是利用Gc的DrawText函数绘制笔墨。可是同时用一张位图作为一个缓冲区存储比来绘制的笔墨。既能撑持年夜字符汇合,速率也很快。
假如必要进修图形和间接写屏的基本,请参考ProgrammingGamesinC++v1.0(www.forum.nokia.com/main/1.6566.21.00.html)。本文次要针对图象类和间接写屏类讲几个简单被疏忽的成绩。
1.4.1.图象类的间接内存会见
贴图是2D游戏最次要的画面操纵。为了完成疾速的贴图,大概完成某种夹杂效果,就不克不及再利用CFbsBitGc的BitBlt大概BitBltMasked举行贴图,而必需本人失掉图片的内存地点,间接读写个中的数据。在读写图片内存地点的过程当中,有几点必要加以注重。
起首,只要当源图片和方针图片色深相称时,才更简单举行贴图操纵。以是,再载进图片的过程当中,我习气把非4k色的图片转化为4k色。之以是选择4k色是由于它也是背景缓冲区的色深。上面的代码经由过程转换能够包管iImage是4k色的图象。
//Makesurethatwehavea4KcolordepthimageiniImage
if(iImage->DisplayMode()!=EColor4K)
{
//Create4kcolorimage
CFbsBitmap*image=new(ELeave)CWsBitmap();
CleanupStack::PushL(image);
User::LeaveIfError(image->Create(iSize,EColor4K));
//Createdevice
CFbsBitmapDevice*device=CFbsBitmapDevice::NewL(image);
CleanupStack::PushL(device);
CFbsBitGc*gc;
User::LeaveIfError(device->CreateContext(gc));
CleanupStack::PushL(gc);
//Bitblttonewcolordepth
gc->BitBlt(TPoint(0,0),iImage);
//Destroycontextanddevice;
CleanupStack::PopAndDestroy();//gc
CleanupStack::PopAndDestroy();//device
CleanupStack::Pop();//image
deleteiImage;
iImage=image;
}
其次,Symbian体系在内存匮乏时会举行碎片收拾。以是假如复杂的用CFbsBitmap::DataAddress猎取内存首地点并入手下手读写,那末大概在你读写的过程当中,图片已被偷偷的挪动了地位,你读写的就是一块有效的内存地区。办理这个成绩的举措是在猎取首地点前,必需先锁定图象内存地区。在高版本的60系列SDK中(好比2.0,2.1),有LockHeap和UnlockHeap函数能够完成这个操纵。可是在低版本的SDK中(好比0.9,1.0),这两个函数是公有的。我们必需经由过程TBitmapUtil锁定内存。可是纷歧定必需利用TBitmapUtil的SetPixel和GetPixel函数举行位操纵。上面是最基础的没有关头色和Alpha通道的复杂贴图代码。
voidCImage::RenderToBitmapL(CFbsBitmap*aBmp,TPointaPos,constTRect&aRect)
{
//在此盘算贴图方针矩形地区
//代码略往
//没有关头色和蒙板的最复杂、最快情形
if(!iKey&&iMask==NULL)
{
//锁定
TBitmapUtilbmpUtil1(ImageL());
TBitmapUtilbmpUtil2(aBmp);
bmpUtil1.Begin(TPoint(0,0));
bmpUtil2.Begin(TPoint(0,0),bmpUtil1);
//猎取首地点
TUint16*addr2=(TUint16*)ImageL()->DataAddress();//sourceimage
TUint16*addr=(TUint16*)aBmp->DataAddress();//targetbmp
TIntline=aBmp->ScanLineLength(
aBmp->SizeInPixels().iWidth,
EColor4K)/2;
TIntline2=iImage->ScanLineLength(//linelengthin16bitword
iImage->SizeInPixels().iWidth,
EColor4K)/2;
//盘算扫描延续量和腾跃量
TIntjump=line-rectw;
TIntlasting2=rectw;
TIntjump2=line2-lasting2;
//猎取贴图首地点
TUint16*p=addr+fromY*aBmp->SizeInPixels().iWidth+fromX;
TUint16*p2=addr2+line2*recty+rectx;
//Thefirstpixeloutofinterest
TUint16*p2end=p2+line2*(toY-fromY-1)+lasting2+jump2;
//入手下手扫描
while(p2!=p2end)
{
//入手下手一个扫描行
TUint16*p2endline=p2+lasting2;
while(p2!=p2endline)
{
//复制一个像素
*p=*p2;
//挪动到下一个像素
p++;p2++;
}
//跳到下一行
p+=jump;p2+=jump2;
}
//解锁
bmpUtil2.End();
bmpUtil1.End();
return;
}
//别的情形。有关头色等等.
//...
最初告知人人几个优化的小秘诀:
利用While轮回间接把指针的对照作为轮回停止前提。不要再多用一个整数来把持轮回。
贴图是个两重轮回,假如你的代码必要判别是不是撑持关头色和Alpha通道等,只管把判别外移到轮回以外。每一个象素都举行好几个if判别的开支太不值得。好比下面的代码,处置最复杂的情形时,while轮回内一个if都没有。
4k色时,RGB内存分列以下图。以是未被利用的4位正巧能够用来存储alpha通道。
1.4.2.间接写屏和特别体系事务
游戏软件一样平常用CDirectScreenAccess举行间接写屏。人人都晓得,WindowServer会在必要中断间接写屏时回调MDirectScreenAccess::AbortNow接口函数,在能够从头启动时回调MDirectScreenAccess::Restart接口函数。但是详细在这两个函数中做甚么,SDK没有过量的先容。我在此说一下我的做法。假如你公道的处置了这两个函数,就能够轻松应对来电、屏保、程序切换等事务。
我们先说AbortNow,它的处置对照复杂。你之需在个中中断驱动游戏逻辑的计时器(通常为个CPeriodic对象),中断声响模块(通常为一个CActive义务)就能够了。
值得费些力量的是Restart函数,它并非在使用程序回到前台,而且能够举行全屏间接写屏时才被回调。以是不克不及在此时果断的恢复游戏逻辑,入手下手游戏。
起首,你要挪用CDirectScreenAccess::StartL()恢复间接写屏。可是必需给这个函数加上TRAP回护。由于它极可能抛出KErrNotReady非常。假如碰到这个非常,那你就间接前往好了,由于间接写屏此时其实不能入手下手。接上去你必要反省一下画图地区,看是不是全部屏幕都能够被利用。假如不是,那也无需启动游戏逻辑,只必要用最初保存的背景缓冲区的内容更新间接写屏地区便可。第三种情形,假如间接写屏乐成启动,而且全部屏幕都能够被绘制,才启动游戏逻辑,启动声响等别的模块。
完全的代码以下:
voidCEngine::AbortNow(RDirectScreenAccess::TTerminationReasons/*aReason*/)
{
//Canceltimeranddisplay
if(iGameTimer->IsActive())
iGameTimer->CancelTimer();
if(!iGameWorldPaused)
{
iGameWorldPaused=ETrue;
iGameWorld->PauseGame();//Pauseaudiostreametc.
}
iPaused=ETrue;
}
voidCEngine::Restart(RDirectScreenAccess::TTerminationReasons/*aReason*/)
{
TRAPD(error,SetupDirectScreenAccessL());
switch(error)
{
caseKErrNone:
break;
caseKErrNotReady:
if(iDirectScreenAccess->IsActive())
iDirectScreenAccess->Cancel();
if(iGameTimer->IsActive())
iGameTimer->CancelTimer();
if(!iGameWorldPaused)
{
iGameWorldPaused=ETrue;
iGameWorld->PauseGame();
}
return;
default:
User::Panic(_L("SetupDSAError"),error);
}
if(iPaused)
{
if(iGameDawingArea==iRegion->BoundingRect())
{
iPaused=EFalse;
if(!iGameTimer->IsActive())
{
iGameWorldPaused=EFalse;
iGameWorld->ResumeGame();
iGameTimer->Restart();
}
}
else
{
PauseFrame();
}
}
else
{
if(!iGameTimer->IsActive())
{
iGameTimer->Restart();
}
}
}
voidCEngine::SetupDirectScreenAccessL()
{
//InitialiseDSA
iDirectScreenAccess->StartL();
//Getgraphicscontextforit
iGc=iDirectScreenAccess->Gc();
//GetregionthatDSAcandrawin
iRegion=iDirectScreenAccess->DrawingRegion();
//Setthedisplaytocliptothisregion
iGc->SetClippingRegion(iRegion);
}
voidCEngine::PauseFrame()
{
//Forcescreenupdate:thisisrequiredforWINS,butmay
//notbeforallhardware:
iDirectScreenAccess->ScreenDevice()->Update();
//anddrawfromunchangedoffscreenbitmap
iGc->BitBlt(TPoint(0,0),&(iDoubleBufferedArea->GetDoubleBufferedAreaBitmap()));
iClient.Flush();
}
};
1.5.声响处置
我的引擎中利用CMdaAudioOutputStream和MMdaAudioOutputStreamCallback完成声响播放功效。它次要有三个类构成:
CAudioStreamPlayer。它复合CMdaAudioOutputStream,承继CActive,完成MMdaAudioOutputStreamCallback接口。我们必要当心的保持缓冲区的巨细以取得低提早播放。CActive不休的创建新的义务,在RunL函数中预算缓冲区中的残剩数据,向个中追加得当的数据,保持缓冲区的预期巨细。
CSimpleMixer。它完成CAudioGenerator接口。由于CMdaAudioOutputStream是一个单一的流式播放器,以是必要写一个混音器举行波形夹杂。这里波形夹杂就是复杂的数据相加。混音器有很多的声道(channel)。每一个channel纪录了个中的CAudio指针和以后播放地位。
CAudio。包括一个音频缓冲区。对每一个声响文件,我们还必要一个类把它载进到内存缓冲区中。
我不会在此解说怎样完成音频播放,那必要独自的一篇文章。假如你也利用这类办法完成声响播放,我只想在此和人人会商两个成绩。
必要进修声响基本的话,能够参考www.newlc.com/article.php?id_article=113。(惋惜我事先进修声响时那篇文章和代码找不到了)
1.5.1.声响的封闭和开启
由于全部音频体系是一个拉的布局,音频流从混音器那边拉数据,混音器从音频缓冲区中拉数据。以是,只需把CMdaAudioOutputStream和写数据的CActive对象delete失落,声响播放就全体中断了。在我的完成中,也就是deleteCAudioStreamPlayer对象便可。再想要开启声响,只必要从头创立这个对象。
这个完成的优点是程序的别的部分不必要保留声响是不是开启这个形态。由于CAudio和CSimpleMixer对象是存在的,CAudio就能够把本人拔出到Mixer的channel中,以为本人仿佛在播放一样。实在由于CAudioStreamPlayer基本没有从Mixer向外拉数据,声响设备是完整中断的。
可是在恢复声响播放时有一点必要注重,恢复前必要清空混音器中的声响数据。由于经由了长工夫的运转,混音器中的各个channel中已塞满了各类声响。假如此时俄然翻开,会传出各类提早了的杂音。
1.5.2.特别毛病处置
MMdaAudioOutputStreamCallback接口中的几个回调函数MaoscOpenComplete、MaoscBufferCopied和MaoscPlayComplete都有一个毛病码参数。你不克不及疏忽这个参数。
好比MaoscPlayComplete函数,是在音频中断播放时被挪用。中断播放的缘故原由多是多种多样的。我们都晓得要处置KErrUnderflow这个情形,这个毛病吗意味着混音器没有实时的供应它音频数据。此时必要从头启动声响流。可是另有一些情形好比KErrDied和KErrInUse很简单被疏忽。KErrDied产生在接听德律风时,此时声响线程已逝世了,那末就必要重修全部音频体系。KErrInUse产生在收到短信时,此时声响设备被抢占,用来播放短信提醒音。此时你也必要重修全部声响体系,可是此时不克不及立即重修,不然仍是一样的了局。你应当守候几秒钟以后才重修它。
下面说的重启声响流和重修声响体系深度分歧。重启声响流在稍后的代码中能够看到。个中RunAudioL向音频流写进了第一个声响缓冲区。重修声响体系在我的完成中就是指先delete再NewL创立CAudioStreamPlayer对象。
这三个毛病的处置代码以下:
//AudiostreamAPIcallback:Calledwhenplaybackhasfinished.
voidCAudioStreamPlayer::MaoscPlayComplete(TIntanError)
{
if(m_bInDelay)
return;
//Ifwefinishduetoanunderflow,we"llneedtorestartplayback.
//NormallyKErrUnderlowisraisedatstreamend,butinourcasetheAPI
//shouldneverseethestreamend--wearecontinuouslyfeedingitmore
//data!Manyunderflowerrorsmeanthatthelatencytargetistoolow.
if(anError==KErrUnderflow){
iObserver->MasoMessage(_L("PlayUnderflow"));
//Thenumberofsamplesplayedgetsresettedtozerowhenwerestart
//playbackafterunderflow
iBaseSamplesPlayed=iSamplesWritten;
//Stopandrestart
iStream->Stop();
Cancel();
#ifdefRATE_16K
iStream->SetAudioPropertiesL(TMdaAudioDataSettings::ESampleRate16000Hz,
TMdaAudioDataSettings::EChannelsMono);
#else
iStream->SetAudioPropertiesL(TMdaAudioDataSettings::ESampleRate8000Hz,
TMdaAudioDataSettings::EChannelsMono);
#endif
iStream->SetVolume(iStream->MaxVolume()/4);
TRAPD(error,RunAudioL());
if(error!=KErrNone){
User::Panic(KPlay,error);
}
return;
}
elseif(anError==KErrDied)
{
m_bInDelay=ETrue;
m_RebuildDelay=0;//nodelay
}
elseif(anError==KErrInUse)
{
m_bInDelay=ETrue;
m_RebuildDelay=3000;//delay3second
}
elseif(anError!=KErrNone){
//Someothererror,panic!
User::Panic(KPlayComplete,anError);
}
}
由外界发明m_RebuildDelay标记,重修CAduioStreamPlayer这个对象。
除MaoscPlayComplete,我在MaoscBufferCopied中疏忽了KErrUnderflow和KErrAbort毛病。在MaoscBufferCopied和MaoscOpenComplete也处置了KErrInUse毛病。
经由下面的处置,我的程序已能够平安的应对来电、短信、切换程序等特别情形了。
作者简介:
姓名:冯兆麟
网平易近:Simba
E-mail:kingsimba@tom.com
团体主页:www.fsgame.netcosoft.org.cn/projects/fslib
本文来自:http://blog.csdn.net/emag_mobile/archive/2005/02/23/298840.aspx
关于iOS开发的学习打个比方就像把汽车分解最底层的原料有塑料钢铁再用这些底层的工具造出来发动机座椅最后再加上写螺丝胶水等把汽车就拼起来了iOS基本都是英文的资料 iPhone文件系统NSFileManager讲解是本文要介绍的内容,主要是通过iphone文件系统来学习NSFileManager的使用方法,具体内容来看本文详解。 因为我们老师也是自学的,给我们讲课说的最多的就是百度,谷歌,查文档。 每个行业都一样,想要一天学有所成是不可能的,一定要做好努力的准备,做ios不是简单的学会oc语言。不怕多走弯路,就怕不肯动手。 down下code4app网站的每个分类的代码挨着看 在此,某不才愿将安装成功的Mac OS X系统的vmware虚拟机向有志学习iOS开发的各位学友们免费开放出来,经测试,可以在WindowsXP/Win7系统上完美运行,即便你的机器只有2GB内存。 培训时可以选择安卓,iOS,Java,因为实习的时候我选了安卓,当时实习时间只有三周,学的晕头转向,而java我也没学过,iOS的基础是C语言,这个大学里还是学过的,于是选择了iOS。 其实在培训的过程中,学习到最多的就是查资料的方式,当时感觉老师好坑,什么都不告诉我们,让我们自己去查,但是现在觉得还是要自己解决问题,这样才能理解的更加深入。 要学会通过各种方法将面前的事情变成自己感兴趣的,那专研起来就不会是无聊和折磨了。 down下code4app网站的每个分类的代码挨着看 同很多iOS开发者一样,我也是通过培训进入到iOS开发这个行业,开始没有打算培训,只准备自己学习一些计算机编程相关的知识,毕业时找一份编程相关工作(本人是信息与计算科学这个专业,是数学系)。 以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。 每个行业都一样,想要一天学有所成是不可能的,一定要做好努力的准备,做ios不是简单的学会oc语言。不怕多走弯路,就怕不肯动手。 自从苹果公司开放iOS SDK以来,大量的国内外的软件开发者将关注的目光聚集在苹果的iOS平台上。由于iPhone和iPad自一出现就给人带来了颠覆性的感觉 我也从简单的状态栏适配开始,先研究了下关于状态栏的适配,特总结如下,供广大网友一起讨论交流。 看《iPhone 4与iPad开发基础教程》,跟着一步步来 每个行业都一样,想要一天学有所成是不可能的,一定要做好努力的准备,做ios不是简单的学会oc语言。不怕多走弯路,就怕不肯动手。 从C语言入门,因为IOS开发用的是OC语言,是在C基础上的,不过也跟C不是很搭界,你可以直接学习OC语言也可以, 重要的是,放眼全球也的确找不到第二个如苹果iOS平台这样健壮、完整、先进而且为开发者带来真实收益的开发平台来。 培训时可以选择安卓,iOS,Java,因为实习的时候我选了安卓,当时实习时间只有三周,学的晕头转向,而java我也没学过,iOS的基础是C语言,这个大学里还是学过的,于是选择了iOS。
页:
[1]
2