1. 程式人生 > >cocos2dx 2.x版本在android下CCLabelTTF的一個bug

cocos2dx 2.x版本在android下CCLabelTTF的一個bug

cocos2dx在android下是採用Paint來生成圖片然後在CCLabelTTF裡顯示的,它具體的程式碼都在java類Cocos2dxBitmap裡,生成完成之後會呼叫一個jni函式將結果傳給cpp層,cpp層靠一個static變數來與java層交換資料,具體如下

       BitmapDC &dc = sharedBitmapDC();

        CC_BREAK_IF(! dc.getBitmapFromJava(pText, nWidth, nHeight, eAlignMask, pFontName, nSize));

        // assign the dc.m_pData to m_pData in order to save time
        m_pData = dc.m_pData;
        CC_BREAK_IF(! m_pData);

這裡有個問題,sharedBitmapDC是一直共用的,它的dc.m_pData永遠儲存的是上一次的資料,如果某種原因java層呼叫的時候失敗了,那麼cpp層繼續的話,就會拿到上上次的資料,但是這個資料是由cpp層負責free的,於是就會出現double free導致程式崩潰。我們在實際中就碰到了這個情況,當時崩潰的棧告訴我們的是CCImage析構裡出現問題
CCImage::~CCImage()
{
    CC_SAFE_DELETE_ARRAY(m_pData);
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    CC_SAFE_DELETE(m_ft);
#endif
}

可是單純看CCImage的程式碼是看不問題的,而且如果這裡有問題,那麻煩大了。最終檢查了之後還是確認cpp部分可能會隱藏bug,於是將上面CCImage裡修改如下
        BitmapDC &dc = sharedBitmapDC();

        CC_BREAK_IF(! dc.getBitmapFromJava(pText, nWidth, nHeight, eAlignMask, pFontName, nSize));

        // assign the dc.m_pData to m_pData in order to save time
        m_pData = dc.m_pData;
        dc.m_pData = NULL;
        CC_BREAK_IF(! m_pData);

這樣一來,cpp層肯定不會再出現double free了,於是再測,這個時候發現java層拋異常了,之前的異常由於程式退出根本就沒列印得出來。再去追查java層,發現具體原因是

setContentSize之後,java層根據ContentSize判斷出來,字型根本放不進去,於是它認為創建出來的Bitmap的高度是0,而Bitmap根本不允許建立高度為0的,於是異常了。我解決的辦法是至少讓它顯示出來一行一列,這樣我們看到顯示就知道哪裡出問題了,而不是直接崩潰。

從這個問題再一次印證了,fail early, fail loud的程式設計習慣,否則一個簡單問題就變成複雜問題了。