對於一些Android開發過程中總有些很坑爹的錯誤,繞了很大一圈,最后發現是一行代碼放錯位置,或者少了幾句聲明等等。
在此,我就分享下個人在android開發過程中遇到的一些問題,也是作為一份備忘~
調試Android程序的方法(我都是用真機調試,模擬器太慢了,不爽):
- 我會用System.out.println();作為信息打印,類似與單片機調試中的串口打印,可以直觀的看到程序執行到哪里
- 我會查看運行過程中的error,幫助自己定位錯誤和網上搜資料(雖然有很多錯誤考看這個還是摸不着頭腦,但對有些簡單的錯誤還是很有幫助的)
- 在調一些網絡通信的程序時,我會在PC端寫一個服務端java程序,有時還會用到wireshark這個網絡數據包分析軟件,這個工具可以清楚地看到網絡中實際的數據,很方便
- 在調試多進程、有關SD等方面的程序時,我偶爾會用DDMS來對程序進行監測
以上4點中,我一般用前三種就夠了,第4種網上很多人說是什么開發Android必備的,不過我用的很少,可能是我水平還不到家吧
問題匯總:
1、使用UDP來進行網絡通信時,由於其receive方法是阻塞的,對於由發送轉為接收模式的應用程序,如果發送請求丟包,則該程序會一直阻塞,而不再發送請求,一個實用的方法是UDP套接字的setSoTimeout();方法,可以設定其接收等待時間,以便超時后再次發送請求,保證其可靠性。
2、使用Google的Map-API來進行開發時,<uses-library android:name="com.google.android.maps" />的聲明要放在<application></>標簽對中,這個是對調用的lib庫的聲明,不像對授權的聲明可以放在<application></>的外面,我就說這里搞錯了,然后搞了一天,程序還是打開錯誤
1 <application android:icon="@drawable/icon" android:label="@string/app_name"> 2 <activity android:name=".ParkingWorld" 3 android:label="@string/app_name"> 4 <intent-filter> 5 <action android:name="android.intent.action.MAIN" /> 6 <category android:name="android.intent.category.LAUNCHER" /> 7 </intent-filter> 8 </activity> 9 <activity 10 android:name=".HomeActivity" 11 android:label="@string/location"> 12 </activity> 13 <activity 14 android:name=".LocationMap" 15 android:label="@string/location"> 16 </activity> 21 <!-- 聲明需要使用Google Map API --> 22 <uses-library android:name="com.google.android.maps" /> 23 </application>
3、使用Google的Map-API來進行開發時,遇到地圖MapView組件顯示網格,並且有“MapActivity:Couldn't get connection factory client”的error,這可能是由於一下幾個原因造成:(類似文章可參考http://our2848884.blog.163.com/blog/static/1468548342011625102639660/)
a.Google API Key申請。試着用錯誤的Google API Key運行程序,地圖得到的只是空格。
key的申請可以參考http://choha.iteye.com/blog/1132841
這里為了得到正確的密匙有一點要注意,那就是產生key的方式不同會影響key是否能用,下面的方法我用着可以:
在cmd中執行keytool -list -alias androiddebugkey -keystore “你的debug keystore位置” -storepass android -keypass android
b.“INTERNET”使用權限(允許應用程序訪問網絡)正確添加了如下語句:<uses-permission android:name="android.permission.INTERNET"/>
c.使用Google地圖的函數庫語句正確添加了。 <uses-library android:name="com.google.android.maps"/>,且置於</application>標簽前。
4、使用DatagramSocket類創建UDP的套接字,使用Socket、SeverSocket進行TCP的套接字通信時,使用完后要記得調用其close方法,否則端口會被一直占用,導致錯誤;而且close語句的位置也很重要,我在一個activity中要多次使用套接字來發數據,本想使用一個套接字解決問題,在最后的Stop();或者Destory();方法中將其close,但結果行不通,而我創建一個進程,進程每次執行開始創建一個,進程執行結束時套接字close,這樣程序就不會報錯,且穩定性也不錯,對於那些時常需要刷新數據的應用來說很實用。
5、使用TabHost時,要注意在TabActivity中不能有setContentView(R.layout.main);這樣的設置布局的語句,因為TabHost其本身就是用來設置布局的,否則會產生以下錯誤:Caused by: java.lang.RuntimeException: Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'
6、當你在A工程中調試X.java時,如果你同時在eclipse中有另一個B工程中也有同名文件X.java,則在雙擊打印出來的錯誤信息時,eclipse可能會幫你定位到B工程的X.java中,讓你看了半天也沒找到哪里有錯誤,這時你可以先把B工程從eclipse刪去,再重新雙擊打印出來的錯誤信息,eclipse就會幫你正確定位啦。
7、java中的乘方運算用Math.pow(底數, 指數);方法,而不使用"^"符號,"^"在java中表示異或。
8、在使用TextView的setText方法時,注意其重載性,若要要將int整型作為參數時,編譯器認為你的參數是個資源ID,找不到對應的ID時,會出現類似以下的報錯:android.content.res.Resources$NotFoundException: String resource ID #0xfe 而如果你是要顯示這個int型數據,我通常會用 “”+數據 的方法利用“+"符號的重載性,將int數據直接轉為String類型。當然,也可以用Integer.valueOf(int i).toString將其轉為String類型。
9、使用套接字傳數據使用以下方法時,雖然String類型中是用char型來存儲數據的,但在使用getBytes()方法后,有的數據,比如char型0x0001會變為一個字節的byte型0x01,而不是兩個字節0x00和0x01,但很多時候其還是會變為兩個字節,比如中文編碼。這可以用wireshark抓包來看,當然也可以直接調用類的length()方法,再打印查看。
1 byte dataSend[] = str.getBytes(); //把傳輸內容分解成字節*/3 //創建一個DatagramPacket對象,並指定要講這個數據包發送到網絡當中的哪個、地址,以及端口號 4 DatagramPacket packetSend = new DatagramPacket(dataSend,dataSend.length,serverAddress,dstPort); 5 //調用socket對象的send方法,發送數據 6 socket.send(packetSend);
因此在用一個字節表達數據時,為防止String轉byte[]時的不確定性,我就直接用byte[]賦給dataSend[],也可以使用String的getBytes(String charsetName)方法
1 byte by[] = new byte[4]; 2 by[0] = ledNum; 3 by[1] = (byte)(Integer.parseInt(strR1)); 4 by[2] = (byte)(Integer.parseInt(strG1)); 5 by[3] = (byte)(Integer.parseInt(strB1)); 6 byte dataSend[] = by; 7 //創建一個DatagramPacket對象,並指定要講這個數據包發送到網絡當中的哪個、地址,以及端口號 8 DatagramPacket packetSend = new DatagramPacket(dataSend,dataSend.length,serverAddress,dstPort); 9 //調用socket對象的send方法,發送數據 10 socket.send(packetSend);
10、在使用TabHost時應注意其Activity的生命周期,如果不加 Intent.FLAG_ACTIVITY_CLEAR_TOP 這個flag,則Tab之間切換時,Activity只是0nPause();而不執行onStop();和onDestroy(); 並且在設置 addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 之后,切換到另一個Activity會調用另一個Activity的OnCreat方法
1 tabHost.addTab(tabHost.newTabSpec("tab2").setIndicator(getString(R.string.park), 2 getResources().getDrawable(android.R.drawable.star_on)) 3 .setContent(new Intent(this, ParkActivity.class) 4 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))); //這句用來清空之前所有的activity
11、從XML文件中獲取字符串的方法 getString(R.string.my_str);
12、在 handlerSocket.post(update); 之后又在update中遞歸調用 handlerSocket.postDelayed(this, 2000); 則可能會導致跳轉到另一個Activity時,update仍會一次又一次地執行,這時可以在跳出Activity的觸發函數,如onPause,onStop函數中加入 handlerSocket.removeCallbacks(update); 如果還是不行的話可以使用全局開關變量,runFlag(自己取的名字)。在跳入Activity的觸發函數,如onCreat,onResume函數中runFlag=1,而在跳出Activity的觸發函數runFlag=0。並且使用runFlag來控制 handlerSocket.postDelayed(this, 2000); 如下列代碼
1 @Override 2 protected void onCreate(Bundle savedInstanceState) 3 { 4 ...... 5 runFlag = 1; 6 ...... 7 ...... 8 update =new Runnable(){ 9 @Override 10 public void run() { 11 ...... 12 if(runFlag == 1){ 13 handlerSocket.postDelayed(this, 2000); 14 } 15 } 16 handlerSocket.post(update); 17 } 18 @Override 19 protected void onPause() { 20 // TODO Auto-generated method stub 21 super.onPause(); 22 //handlerSocket.removeCallbacks(update); 23 runFlag = 0; 24 }
未完待續。。。