【原创】对于一些Android开发过程中坑爹、细小,但又重要的错误的总结
对于一些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 }
未完待续。。。