使用ant編譯apk檔

在一般的狀況下最為人所知產出apk的方法是使用ADT eclipse plugin功能來產出APK檔
(Android Tools->Export Signed/Unsigned Application Package)
但除此之外android也提供開發者經由ant產出apk檔
mac中有內建ant
所以可以在mac的終端機下指令來產出apk檔

那為何要使用ant
如果今天要產出多種版本 你必須要打開你的eclipse分別打開多個專案然後Export Signed/Unsigned Application Package
等於同樣步驟要作三次
而且還需要輸入密碼
而且你必須待在電腦旁邊

如果今天用ant來編的話 你可以指定一次編多種版本 也不用在輸入任何東西(前提是有寫到設定檔)
像我之前有產出10個檔案的需求(五個market api * 2(debug/release))
通常在離開公司時執行
隔天就可以寄出檔案了

以下寫一些在cocos2d-x中使用ant的經驗
使用cocos2d-x建立的android專案中
會比一般的android project多出以下的檔案
build.xml - ant使用的專案設置
local.properties - 裡面會有sdk的路徑設定值
ant.properties - 預設是空的 但可以在這裡設置編apk檔時使用的key相關設定
要注意的是如果你的android專案有使用其他的android專案當作library
那個專案也需要有這三個檔案

編譯的方式是在終端機下指令

ant <target>

如果沒有給target的話 會跑設定的預設target

在build.xml當中會匯入另一個custom_rules.xml,如果沒有的話就不會處理custom_rules.xml


在這裡的註解中有提到你可以在custom_rules.xml使用的target

-pre-build
-pre-compile
-post-compile
-post-package
-post-build
-pre-clean

基本上就如同target的名稱一樣 你在哪個階段有需求就去寫那個target

比方說我有需求是將編出來的檔案集中到一個資料夾中並讓檔名出現編譯時間, 版本資訊
所以我用到-post-build 內容如下

<target name="-post-build">
      <tstamp>
          <format property="package.time" pattern="yyyyMMdd_HHmm"/>
      </tstamp>
      <property name="apk.copy.name" location="../android_build_apk/${ant.project.name}_${apk.tag}_${build.android.param}_${package.time}.apk" />
      <echo>copy ${out.final.file}</echo>
      <echo>to   ${apk.copy.name}</echo>
      <copy file="${out.final.file}" tofile="${apk.copy.name}"/>
</target>

上面這個target會將編譯產出的apk複製到指定目錄並變更檔名
build.android.param 這個property是設定編譯時是測試版(debug)還是正式版(release)
apk.tag是我設定在ant.properties中用來表示這是那個市場的版本
這樣產出來的檔名會是project.name_xxx_release_20130705_1453.apk的樣子

如果是產出signed Application Package
在編譯的過程中會要求你輸入key的相關資訊
表示你產出多個檔的話就要輸入多次
其實可以把相關需要的參數設定到ant.properties中
就不需要輸入了
詳細的參數名稱可以參閱${sdk.dir}/tools/ant/build.xml
看看需要哪個property
我的狀況如下

key.store=../libs/android/keystore/igsgames.keystore
key.alias=xxx
key.store.password=xxxxxxxx
key.alias.password=xxxxxxxx
apk.tag=gplay

這樣可以不用每次輸入
但相對表示你把密碼打在檔案中給人看
所以你可以在執行ant時使用-Dproperty=”xxx”這樣的方式輸入property
就不會有這樣的問題了

用ant其實還滿方便但是資料不太多
除了ant in action這本書外就是網站資料
另外在Android Studio中使用Gradie又是不同的系統
不知道下個專案還會不會有機會用ant

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

問題描述

使用版本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檢查)又不是有效的物件
才會造成當機

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

GCM在無該服務的Exception

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

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

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問題紀錄

  1. 問題:
    一次更新後,所有純平版裝置都無法在Google Play中看到該App,該次更新主要提供使用簡訊小額付費機制
    解答:
    http://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鍵
    解答:
    http://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鍵兩種進入讓遊戲進入背景的方法的行為不同
    解答:
    請直接看 http://blog.csdn.net/hunter_hb/article/details/8572095
    解說的十分詳細
    我的設定是orientation 修改後解決

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

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

問題

在某次上市的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/GT-I9000/GT-I9000:2.3.5/GINGERBREAD/XXJVT:user/release-keys'
    I/DEBUG   (  135): pid: 11056, tid: 11056  >>> com.igs.salonbossworld <<<
    I/DEBUG   (  135): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 5103a793
    ……….
    I/DEBUG   (  135):  d30 0000000000000000  d31 3ff0000000000000
    I/DEBUG   (  135):  scr 60000010
    I/DEBUG   (  135):
    I/DEBUG   (  135):          #00  pc 005691b6  /mnt/asec/com.igs.salonbossworld-1/lib/libgame.so
    I/DEBUG   (  135):          #01  pc 0056935e  /mnt/asec/com.igs.salonbossworld-1/lib/libgame.so
    I/DEBUG   (  135):          #02  pc 0056a150  /mnt/asec/com.igs.salonbossworld-1/lib/libgame.so (curl_mvsnprintf)
    I/DEBUG   (  135):          #03  pc 0055d014  /mnt/asec/com.igs.salonbossworld-1/lib/libgame.so (curl_failf)
    I/DEBUG   (  135):          #04  pc 00556586  /mnt/asec/com.igs.salonbossworld-1/lib/libgame.so (curl_resolv_timeout)
    I/DEBUG   (  135):          #05  pc 0056308a  /mnt/asec/com.igs.salonbossworld-1/lib/libgame.so (Curl_connect)
    
  3. curl_mvsnprintf, curl_failf, curl_resolv_timeout部份作搜尋,查得幾篇技術文章有類似狀況如下
    http://sourceforge.net/p/curl/bugs/973/
    http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=617647
    重點在使用cURL並在多執行緒狀況下
    有篇文章說明這個狀況取決於os與thread的實作細節(回文的是官方人員)
    http://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的相關問題
    http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading
    另有一篇中文說明文章 http://www.cppblog.com/tx7do/archive/2012/02/20/166048.html

  4. 依照文章說明有2種作法,一是使用CURLOPT_NOSIGNAL,缺點是會失去DNS Lookup timeout的能力,可用增加c-ares支援解決(但有文章指出某些android裝置會有問題 http://curl.haxx.se/mail/lib-2013-04/0276.html)
    另一個方法是保持同時間只有一個curl handle,缺點需要改動程式架構

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

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