背景
開始行動
踩雷了
拿到編譯出的libB.a,放入engine.so編譯工程中,修改mk文件,頭部加入靜態庫預編譯段,include $(CLEAR_VARS)
LOCAL_MODULE := BModule
LOCAL_SRC_FILES := libB.a
include $(PREBUILT_STATIC_LIBRARY)
在so編譯部分加載BModule模塊:LOCAL_STATIC_LIBRARIES := BModule淚奔了,libB.a和engine.so的編譯過程都自我感覺非常之良好啊。。。尼瑪。只能不斷自我打擊,暗示一定什麼環節出問題了。。沒有crash,文字標註還有,但是底圖一直渲染不成功。。。
排雷的過程
1)將B的源碼放入SO編譯工程,最終so包沒問題。只能懷疑自己的libB.a編譯有問題或者鏈接有問題咯,於是進行第一個嘗試:
不直接進行源碼編譯,而是通過ndk自帶的arm-linux-androideabi-ar.exe工具,將源碼編譯時產生的一系列.o文件,手工編譯成.a,然後鏈接這個.a,發現build的so包還是有問題。
source => *.o => engine.so
*.o => libB.a => engine.so
ar
上述兩種路徑:第一條表示源碼編譯,ok;第二條是源碼編譯的中間結果.o文件,手工通過ar打包成 libB.a,然後鏈接libB.a,就有問題。真是見鬼了。!。
2)躲不過了,只能source中增加log,第一次build成libB.a,然後第二次build成engine.so,最後拷貝到android工程中,build APK。
source => libB.a => engine.so => apk.
整個蛋疼的定位過程得益於windows的批處理腳本,可以實現半自動化。
不斷重複這個過程,不斷調整log精度。最終定位到底圖瓦片繪製失敗的問題:座標轉化函數GetGeoRect的結果錯誤,導致繪製時候取不到數據。
定位問題的原因
跟組內一經驗豐富的哥們討論,那天恰好週五,下班前還是沒結果。。。晚上回去後回一直在回想編譯的整個過程,想起他無心的一句話:“是不是可能有重複的定義啥的“。終於想到了一個問題,A工程裏面B工程的兩個頭文件,當時爲了解耦其他人將兩個頭文件重複拷貝了一份,(明顯觸犯了DRY原則)如下, yy.h中包含了靜態函數的GetGeoRect定義,vv.h中包含了render_config_t結構體定義,而GetGeoRect中使用了render_config_t結構體。
我最近一次B模塊升級,更新了vv.h中的render_config_t結構體,內部增加了一個256的char數組。。
附圖新舊vv.h頭文件中的render_config_t結構體:
舊的:新的:
第二天週六,按耐不住奔到公司,更新A模塊中的vv.h頭文件,build出的so包終於正確了。總算是找到問題所在了:
A和B工程中的vv.h和yy.h文件重複,B中vv.h文件最近被更新過。
1)當A、B工程均採用源碼編譯時,最終SO中的GetGeoRect函數內部使用了最新的render_config_t結構體佈局(編譯器可能根據文件的修改時間等等作爲比對條件吧?),因此底圖繪製正確。
2)但是當B工程build成靜態庫libB.a時,此時build成SO時,GetGeoRect函數定義採用了A工程中源碼(編譯器可能更加信賴源碼吧),因此render_config_t也採用了舊的內存佈局。因此當調用SO運行時,傳入GetGeoRect函數的render_config_t的對象採用最新的內存佈局,但是內部實際上是按舊的結構體解析和執行,當然結果就完全錯了。