雜物聚集地

cocos2d-x i9100 音效播放當機問題

Tue, 06 Aug 2013 06:56:22 GMT

問題描述

使用版本cocos2d-x 2.1.3 特定機型 i9100音效播放時當機

錯誤訊息

I/DEBUG (13020): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG (13020): Build fingerprint: 'samsung/GT-I9100/GT-I9100:4.0.4/IMM76D/ZSLPQ:user/release-keys'
I/DEBUG (13020): pid: 14187, tid: 14200 >>> xxx.xxxxxx.xxxx <<<
I/DEBUG (13020): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
.....
I/DEBUG (13020): #00 pc 00521476 /data/data/xxx.xxxxxx.xxxx/lib/libgame.so (_ZN12OpenSLEngine16setEffectLoopingEjb)
I/DEBUG (13020): #01 pc 00521dfa /data/data/xxx.xxxxxx.xxxx/lib/libgame.so (_ZN23SimpleAudioEngineOpenSL10playEffectEPKcb)

經查詢後發現cocos2d-x引擎在i9100上使用OpenSL(僅有這一隻)
故只有i9100出現此問題

修改方式

先說這不是個好做法

OpenSLEngine.cpp

void OpenSLEngine::setEffectLooping(unsigned int effectID, bool isLooping)
{

    if(sharedList().find(effectID) == sharedList().end()) return; //<--add this

    SLresult result;
    vector* vec = sharedList()[effectID];
    assert(NULL != vec);

    // get the first effect player that to be set loop config
    vector::iterator iter = vec->begin();
    AudioPlayer * player = *iter;

主要原因應該是sharedList()[effectID];取vector*時沒有該key的物件
但是vector這樣取會自行幫你產生一個物件
但該物件雖然不是NULL(故可避過assert檢查)又不是有效的物件
才會造成當機

引發的原因查詢後認為是如 https://www.cocos2d-x.org/boards/6/topics/26439 網頁上所講的一樣
因為i9100有音效數量限制所引發
之所以說這不是好做法是因為應該要減少使用音效數
或者作動態載入
緊急的狀況下只能先這樣做
但這樣做會有一些音效播不到


GCM在無該服務的Exception

Mon, 08 Jul 2013 07:04:20 GMT

問題
把目前運行測試正常的檔案送給大陸廠商,卻發現無法進入遊戲

解答
後來弄到了出現問題的手機運行後發現下面的錯誤訊息

Caused by: java.lang.UnsupportedOperationException: Device does not have package com.google.android.gsf
E/AndroidRuntime( 787): at com.google.android.gcm.GCMRegistrar.checkDevice(GCMRegistrar.java:98)

須處理沒有該服務時例外狀況

重點在於大陸手機多沒有google服務 所以GCM部分需要處理 UnsupportedOperationException

所以

  1. 在用google相關服務時須要考慮沒有該服務時的狀況(尤其大陸地區)
  2. 相關服務須要找替代品(金流是很好找 但推播就真的比較少聽過了)

Android問題紀錄

Fri, 31 May 2013 14:10:21 GMT
  1. 問題:
    一次更新後,所有純平版裝置都無法在Google Play中看到該App,該次更新主要提供使用簡訊小額付費機制
    解答:
    https://developer.android.com/guide/topics/manifest/uses-feature-element.html
    因為簡訊寄送需要增加SEND_SMS這個Permission,連帶會自動將android.hardware.telephony這個feature設為需求
    解法為在 androidmanifest中增加<uses-featureandroid:name="android.hardware.telephony"android:required="false"/>

  2. 問題:
    遊戲當中並沒有使用到menu鍵,但是只要手機沒有實體menu鍵的話都會出現虛擬menu鍵
    解答:
    https://stackoverflow.com/questions/7035325/how-i-hide-the-menu-button-for-one-activity
    之前我沒有設定targetSdkVersion,由於targetSdkVersion預設等於minSdkVersion,而我設定的值為9,被判定為legacy,則會顯示虛擬menu鍵
    targetSdkVersion設為14後則解決

  3. 問題:
    targetSdkVersion換成14後的測試期間,發現home鍵與power鍵兩種進入讓遊戲進入背景的方法的行為不同
    解答:
    請直接看 https://blog.csdn.net/hunter_hb/article/details/8572095
    解說的十分詳細
    我的設定是orientation 修改後解決


專案中與cURL相關瀕繁當機處理記錄

Thu, 30 May 2013 14:15:23 GMT

以下是專案中遇到的一個比較特別的問題 記錄如下

問題

在某次上市的android版遊戲有瀕繁當機的狀況,通常是在進遊戲後不久,而這狀況在內部測試時並不常出現,且無發生在iOS版本

處理

  1. 本次新上市版本增加公告功能

由cURL進行http連線取得公告設定,檔案與圖片此部分使用cocos2d-x extensions的network套件,遊戲本身server連線程式也由cURL寫成
2. 測得兩次進遊戲後當機(花三天測試出現兩次),看似curl程式庫引發,錯誤訊息如下:

Fatal signal 11 (SIGSEGV) at 0x5103a793 (code=2)
I/DEBUG   (  135): *** *** *** *** *** *** *** *** ** *** *** *** *** *** *** ***
I/DEBUG   (  135): Build fingerprint: 'samsung/T-I9000/GT-I9000:2.3.5/GINGERBREAD/XXJVT:user/elease-keys'
I/DEBUG   (  135): pid: 11056, tid: 11056  >>> com.gs.salonbossworld <<<
I/DEBUG   (  135): signal 11 (SIGSEGV), code 2 SEGV_ACCERR), fault addr 5103a793
……….
I/DEBUG   (  135):  d30 0000000000000000  d31 ff0000000000000
I/DEBUG   (  135):  scr 60000010
I/DEBUG   (  135):
I/DEBUG   (  135):          #00  pc 005691b6  /mnt/sec/com.igs.salonbossworld-1/lib/libgame.so
I/DEBUG   (  135):          #01  pc 0056935e  /mnt/sec/com.igs.salonbossworld-1/lib/libgame.so
I/DEBUG   (  135):          #02  pc 0056a150  /mnt/sec/com.igs.salonbossworld-1/lib/libgame.so curl_mvsnprintf)
I/DEBUG   (  135):          #03  pc 0055d014  /mnt/sec/com.igs.salonbossworld-1/lib/libgame.so curl_failf)
I/DEBUG   (  135):          #04  pc 00556586  /mnt/sec/com.igs.salonbossworld-1/lib/libgame.so curl_resolv_timeout)
I/DEBUG   (  135):          #05  pc 0056308a  /mnt/sec/com.igs.salonbossworld-1/lib/libgame.so Curl_connect)
  1. curl_mvsnprintf, curl_failf, curl_resolv_timeout部份作搜尋,查得幾篇技術文章有類似狀況如下

https://sourceforge.net/p/curl/bugs/973/
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=617647
重點在使用cURL並在多執行緒狀況下
有篇文章說明這個狀況取決於os與thread的實作細節(回文的是官方人員)
https://curl.haxx.se/mail/lib-2002-12/0103.html
> AFAIK, it isn't always specified what happens with signals in a multi-threaded system, it depends on your OS and your thread implementation. Which thread receives the signal etc.

另外官方有說明Multi-threading的相關問題  
https://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading    
另有一篇中文說明文章  https://www.cppblog.com/tx7do/archive/2012/02/20/166048.html  
  1. 依照文章說明有2種作法,一是使用CURLOPT_NOSIGNAL,缺點是會失去DNS Lookup timeout的能力,可用增加c-ares支援解決(但有文章指出某些android裝置會有問題 https://curl.haxx.se/mail/lib-2013-04/0276.html)

另一個方法是保持同時間只有一個curl handle,缺點需要改動程式架構

  1. 基於幾點推測當機可能符合該BUG,之所以說是推測是因為無法確認玩家當機的確切原因且測試時無法完全重現

    • 連server的程式都以pthread開執行緒並以curl程式庫作http連線,當遊戲剛進去有可能會更新信件與動態公告資料,此時多執行緒成立。
    • 由於取決於這個狀況取決於os與thread的實作,或許可以解釋為何iOS無該狀況發生,猜測玩家之所以比較容易發生是因為玩家通常在信件上資料會比較多或者是玩家常處於不穩定網路狀況故較容易逾時。
  2. 討論後將android動態公告http連線方式由cURL改為android原生方法,新版上市後已無該狀況。


修改cocos2d-x提供的build_native.sh

Thu, 23 May 2013 14:41:27 GMT

在cocos2d-x專案中要編譯native code時會用到build_native.sh來做編譯,這個Bash腳本會使用NDK的ndk-build

隨著專案越來越複雜有些需求需要特別去處理,以我自己的狀況來講,須要分為Debug與Release兩個版本以及分為國際版與韓國版,上述兩個部分都可以經由修改build_native.sh來達到,下面會分成幾個部分來說:

先列出幾個教學網站
https://www.ibm.com/developerworks/cn/linux/l-bash-parameters.html
https://linuxconfig.org/Bash_scripting_Tutorial#18-redirections
https://linux.vbird.org/linux_basic/0340bashshell-scripts.php
我都是有需要在去查

基本上這個部份我是在版本2.0.3加的 但是最新的2.1.3應該也可用

選項修改

原本build_native.sh中的選項部分的程式碼如下
其實要不是因為要改這個部份我還不知道這個腳本有選項可以用

while getopts "sh" OPTION; do  
case "$OPTION" in  
s)  
buildexternalsfromsource=1  
;;  
h)  
usage  
exit 0  
;;  
esac  
done  

上面的寫法主要是處理"-s"這種型態的選項,也就是說如果今天的的指令是長下面這樣:
bash build_native.sh -s xxx
就只會處理-s的部分。

原本是想說就再加一個選項,結果並不如我想的這樣簡單,基本上先看到下面跟ndk-build有關的程式碼如下

"$NDK_ROOT"/ndk-build -C "$APP_ANDROID_ROOT" $* \
NDK_MODULE_PATH=${COCOS2DX_ROOT}:${COCOS2DX_ROOT}/cocos2dx/platform/third_party/android/source"  

重點在於$*代表該腳本的所有參數,如果今天我用了一個ndk-build已經有的參數就會影響ndk-build的行為,如果用了一個沒有的又會說是一個invalid option。

此時有兩個做法 一個是把 $*拿掉,但是這樣有一個缺點,原本可以下如下指令
bash build_native.sh clean,也就是ndk-build clean,就會變成不能用。

另一個做法就是在選項的部分作修改,以下是我的改法:

for p in $*
do
     echo "$p"
     case $p in
          "kr")
               echo "force build kr"
               forcebuildkr=1
               #Can not pass this parameter to next call bash
               ;;
          "debug")
               echo "get debug"
               debugflag=1
               #Can not pass this parameter to next call bash
               ;;
          "-s")
               buildexternalsfromsource=1
               param="$param$p "
               ;;
          "-h")
               usage
               exit 0
               ;;
          *)
               param="$param$p "
               ;;
     esac
done  

這是另一種選項的做法,以上面提到的bash build_native.sh -s xxx,在這裡就會全都處理到,又由於我不想全部選項都傳到ndk-build,所以該傳的我就存在$param,再把$*改為$param即可,此時我已經可以透過選項來選擇我要怎樣的版本。

利用Application.mk分不同的版本

在最早的時候,我在分debug與release版的時候是直接去android.mk中修改CPPFLAG,後來覺得實在是太麻煩要改來改去,就採取我現在使用的方法

ndk-build有一個選項:ndk-build NDK_APPLICATION_MK=,可以讓你自己指定要使用的Application.mk,所以我將不同狀況需要的CPPFLAG設定寫到不同的Application.mk檔中,再依照選項不同設定不同的數值去決定到底要用哪一個,大概如下:

if [[ "$debugflag" ]]; then
     echo "debug on!!"
     if [[ "$forcebuildkr" ]]; then
          echo "force build kr!!"
          usedebugappmk="${APP_ANDROID_ROOT}/jni/Application_debug_kr.mk"
     else
          echo "build global!!"
          usedebugappmk="${APP_ANDROID_ROOT}/jni/Application_debug.mk"
     fi
else
     echo "debug off!!"
     if [[ "$forcebuildkr" ]]; then
          echo "force build kr!!"
          usedebugappmk="${APP_ANDROID_ROOT}/jni/Application_kr.mk"
     else
          echo "build global!!"
          usedebugappmk="${APP_ANDROID_ROOT}/jni/Application.mk"
     fi
fi
"$NDK_ROOT"/ndk-build DK_APPLICATION_MK="$usedebugappmk" -C $APP_ANDROID_ROOT" $param \
"NDK_MODULE_PATH=${COCOS2DX_ROOT}:${COCOS2DX_ROOT}/ocos2dx/platform/third_party/android/prebuilt"

控制資源

一般cocos2d-x專案的資源檔都放在Resource資料夾下,後來我遇到了某種情況是某些檔案只在android狀況下改變,所以我另外開個一個資料夾Resource_android,在原本的# copy resources腳本下,添加了一段處理android特有資源的程式:

# copy ver android
for file in "$APP_ROOT"/Resource_android/*
do
if [ -d "$file" ]; then
    cp -rf "$file" "$APP_ANDROID_ROOT"/assets
fi
if [ -f "$file" ]; then
    cp "$file" "$APP_ANDROID_ROOT"/assets
fi
done

另外也可以將腳本寫在別的.sh中在去呼叫如下

if [[ "$debugflag" -ne 1 ]]; then
     echo "delete .lua .bat .exe"
     if [ -e "del_lua.sh" ]; then
          ./del_lua.sh
     fi
fi

專案在做release版時會將lua檔全部刪除,這部分是寫在另一個.sh中。