1、有一個方法,有一段類似於下面這樣:
new Thread(){ public void run(){ // 做了一些數據庫操作
db.close(); } }.start();
結果運行過程中,發現有時候會報數據庫已經鎖定的異常。最后才定位到上面這段代碼。原因是在上面的run()方法中打開了數據庫,這個時候會自動鎖定Database,如果在關閉數據庫之前,另外一個線程B也進行數據庫操作,就會報這個異常。如果數據庫已經關閉之后,另外一個線程B請求數據庫操作,就沒有問題。
所以在新線程中進行數據庫操作,或者是TimerTask中進行數據庫操作的時候,要注意考慮並發的問題,也許就會有數據庫鎖定的異常。
2、另外一段代碼,將TimerTask的銷毀操作放在了Service的onDestory()方法里,但是發現有時候會報TimerTask已經schedule的異常。開始就覺得很奇怪,不是已經在onDestory()方法里關閉了TimerTask了嗎?后來想到,Service的onDestory()方法不是一定有機會執行的。在資源不足的時候,系統會強行關閉Service,在這種情況下,onDestory()方法是沒有機會執行的。
所以,絕對不能依賴組件的onDestory()方法。
3、這次項目管理有一個失誤的地方。就是在項目設計早期,沒有關注數據庫表設計。到了后期才來檢查數據庫的設計,結果發現有很多地方需要變更,這就造成建表語句、模型對象、DAO都要修改,開發和測試的成本都非常高。
所以以后一定要在早期就關注數據庫設計,如果有迫不得已的數據庫變更(這個幾乎是難免的),也要趁早進行
4、有的時候,發現兩個組員都是更新了最新的代碼,可是一個能正常跑起來,另一個人就總是報錯。這種情況,一般是因為數據庫錯誤引起的,雖然是同樣的代碼,但是卻不是同樣的數據庫。這種情況下,需要把舊的應用卸載,重新啟動。這樣就會觸發SqliteDatabaseOpenHelper的onCreate()方法,重新建立一致的數據庫
5、前期把整個應用的常量,都放在一個Constants類里,這樣雖然避免了硬編碼,但是其實由於Constants類里充斥了各種各種的常量,也失去了可維護的意義。所以如果常量太多的話,可以考慮根據模塊或者業務,分散到不同的Constants類里
6、一般跳轉到別的Activity,是調用startActivity()方法,不過如果需要在新Activity里做完某些操作以后,再通知原先的Activity的話,應該調用startActivityForResult()方法,然后實現onActivityResult()方法
7、有一次在DDMS里發現了has leaked window的異常告警。這種情況一般是在Activity里創建了Dialog,然后又沒有及時關閉引起的。解決的辦法很簡單,在需要創建Dialog的地方,調用showDialog()方法,然后在Activity里實現onCreateDialog()方法,這樣的話,Activity就會管理Dialog的生命周期,就不會發生上述的問題了
8、一般連接android底層的linux,用adb shell命令就可以了。但是如果同時開啟了模擬器,又連接了真機,就不能這樣了,必須用以下的命令:
adb devices,這個命令可以看到有哪些設備,包括了模擬器和真機
然后用adb -s (deviceId) shell,就可以連接到目標終端上
或者還有一個辦法,用adb -d shell連接到設備上,用adb -e shell連接到模擬器上
9、我自己定義了一個VO類,可是用Intent.putExtra()方法竟然放不進去,原來是這個VO沒有實現Serializable接口
10、項目中業務上需要很多后台長時間運行的Service,可是后來發現Service實在太多了,所以換了一個方案。把實現業務邏輯的Service,都改成用IntentService實現。只開啟一個長時間后台運行的Service,在這個Service里用多個TimerTask,定時去startService()各個IntentService。這樣應用就只有一個駐留Service了
11、很多Service都去實現onStart()方法,其實這是不對的,onStart()是一個遺留方法。Android2.2以上的版本,官方推薦是實現onStartCommand()方法
12、如果沒有多線程的需求的話,使用IntentService替代Service是一個不錯的選擇。一方面不需要編碼另起線程(Service默認是跑在UI Thread里的,這點相當恐怖),另一方面,也不需要顯式地調用stopSelf(),或者stopService()。IntentService的問題是,如果需要並發地處理請求,則晚來的Intent只能排隊
13、TimerTask比較多的話,一個最佳實踐(或者是迷信)是:用3、7、11、13、17這樣的素數錯開運行的時間
14、一個已經調用了.cancel()方法的TimerTask,不能被再次作為Timer.schedule()的參數,需要重新實例化一個TimerTask
15、沒有特別的需求的話,個人建議可以在AndroidManifest.xml里不配置intent-filter,這樣就強制代碼只能用顯式的Intent來跳轉Activity或者開啟Service,不能用隱式的Intent來調用。
這種做法雖然損失了一些面向組件的靈活性,但是定位問題會比較簡單。因為比較容易查出來,某個Service被哪些組件啟動了,或者某個Activity可以從哪些頁面跳轉過來
當然,如果Activity和Service被設計為允許其他應用使用,那必須要支持隱式的Intent調用
16、數據庫字段的命名,還是有點講究的。比如在T_PERSON表里,字段就沒必要再命名成PERSON_NAME,直接叫NAME就可以了,前者比較冗余。這個雖然是小問題,但是對於追求細節完美的應用來說,還是要注意的。如果前期沒有注意,到項目后期再統一優化,代價會大很多
17、原來經常用getSharedPreference()方法,來獲取自定義SP文件。其實發現,用PreferenceManager.getDefault()方法就可以獲得一個默認的SP文件了
18、DTO和VO,建議重載toString()方法,調試會很方便
19、Activity的findViewById()方法,和View的findViewById()是不一樣的。
前者一般都在onCreate()方法里調用setContentView()方法,然后findViewById()就是從這個xml文件里找ID
后者一般調用一個inflate()方法,然后findViewById()方法是從這個xml文件里找ID