前幾天學習了 Android 下 Socket 編程。
學習 Socket 編程是有目的的,需要完成在手機與 PC 之間的通訊。通訊的內(nèi)容是將手機上播放的 MP3 信息,通過 Socket 傳輸?shù)?PC 端。
在參考網(wǎng)上相關(guān) Socket 的文章后,基本上完成了 Socket 功能。所以就繼續(xù)學習 Android 下音樂播放器的實現(xiàn)。在實現(xiàn)音樂播放器過程中,發(fā)現(xiàn)由于音樂播放器至少要有播放列表和正在播放兩個 Activity,這樣問題就來了:
(1). Socket 只是在第一個 Activity 中實現(xiàn)了,當這個 Activity 活動時沒有問題。但此 Activity 非活動時,不能處理 Socket。
(2). 當反復(fù)進入第一個 Activity 時,會出現(xiàn) Socket 初始化報錯的問題。出現(xiàn)這樣的錯誤,是由于 Sokcet 的初始化放在第一個 Activity 的 onCreate 中。
由于在做音樂播放器時使用了 Service,所以想到用 Serivce 來處理 Socket 應(yīng)該沒有問題。但是否有其它的方法呢?由于個人是剛剛接觸 Android 編程,就不能確定這個問題了!
在論壇提問,得到的答案是:(1) Serivce; (2) 也可以更改activity的啟動方式,讓串口不重復(fù)創(chuàng)建。顯然,第二種方法還沒有接觸過。采用第一種 Serivce 來實現(xiàn)更可靠一些。
首先,實現(xiàn) Socket Service。
package?com.jia.leozhengfirstapp; import?java.io.IOException; import?java.io.InputStream; import?java.io.UnsupportedEncodingException; import?java.net.ServerSocket; import?java.net.Socket; import?android.app.Service; import?android.content.BroadcastReceiver; import?android.content.Context; import?android.content.Intent; import?android.content.IntentFilter; import?android.os.IBinder; import?android.util.Log; public?class?SocketService?extends?Service?{ ??private?Socket?clientSocket?=?null; ??private?ServerSocket?mServerSocket?=?null; ??private?SocketAcceptThread?socketAcceptThread?=?null; ??private?SocketReceiveThread?socketReceiveThread?=?null; ??private?SocketReceiver?socketReceiver; ??public?static?final?String?SOCKER_ACTION?=?"com.jia.Socket.Control"; ??public?static?final?String?SOCKER_RCV?=?"com.jia.Socket.ReceiveStr"; ??private?boolean?stop?=?true; ??@Override ??public?IBinder?onBind(Intent?intent)?{ ????//?TODO?Auto-generated?method?stub ????return?null; ??} ???@Override ??public?void?onCreate()?{ ??????????super.onCreate(); ??????????Log.d("service",?"socket?service?created"); ??????????socketReceiver?=?new?SocketReceiver(); ??????????IntentFilter?filter?=?new?IntentFilter(); ??????????filter.addAction(SOCKER_ACTION); ??????????registerReceiver(socketReceiver,?filter); ??????????socketAcceptThread?=?new?SocketAcceptThread(); ????????????//?開啟?Socket?監(jiān)聽線程 ????????????socketAcceptThread.start(); ???} ??@Override ??public?void?onStart(Intent?intent,?int?startId)?{ ?????Log.d("service",?"socket?service?start"); ??} ??@Override ??public?void?onDestroy()?{ ??Log.d("service",?"socket?service?destroy!"); ??} ??public?class?SocketReceiver?extends?BroadcastReceiver?{ ????@Override ????public?void?onReceive(Context?context,?Intent?intent)?{ ??????String?action?=?intent.getAction(); ????????????if(action.equals(SOCKER_ACTION))?{ ??????????????String?sub_action?=?intent.getExtras().getString("ACTION"); ??????????????if(sub_action.equals("reconnect"))?{ ????????????????Log.d("service",?"socket?service:?reconnect."); ?????????????????socketAcceptThread?=?new?SocketAcceptThread(); ??????????????????//?開啟?Socket?監(jiān)聽線程 ??????????????????socketAcceptThread.start(); ??????????????} ????????????} ????} ??} ??private?class?SocketAcceptThread?extends?Thread ??{ ???????@Override ???????public?void?run() ???????{ ?????????Log.d("service",?"socket?service?-?SocketAcceptThread::run"); ???????????try?{ ???????????????//?實例化ServerSocket對象并設(shè)置端口號為?12589 ???????????????mServerSocket?=?new?ServerSocket(12589); ???????????}?catch?(IOException?e)?{ ???????????????//?TODO?Auto-generated?catch?block ???????????????e.printStackTrace(); ???????????} ???????????try?{ ???????????????//?等待客戶端的連接(阻塞) ???????????????clientSocket?=?mServerSocket.accept(); ???????????}?catch?(IOException?e)?{ ???????????????//?TODO?Auto-generated?catch?block ???????????????e.printStackTrace(); ???????????} ???????????socketReceiveThread?=?new?SocketReceiveThread(clientSocket); ???????????stop?=?false; ???????????//?開啟接收線程 ???????????socketReceiveThread.start(); ?????????????Intent?sendIntent?=?new?Intent(SOCKER_RCV); ?????????????sendIntent.putExtra("action",?"ClientIP"); ?????????????sendIntent.putExtra("content",?clientSocket.getInetAddress().getHostAddress()); ?????????????//?發(fā)送廣播,將被Activity組件中的BroadcastReceiver接收到 ?????????????sendBroadcast(sendIntent); ???????} ???} ???private?class?SocketReceiveThread?extends?Thread ???{ ???????private?InputStream?mInputStream?=?null; ???????private?byte[]?buf; ???????private?String?str?=?null; ???????Socket?sUsed; ???????SocketReceiveThread(Socket?s) ???????{ ?????????Log.d("service",?"socket?service?-?SocketReceiveThread"); ???????????try?{ ???????????????//?獲得輸入流 ???????????????this.mInputStream?=?s.getInputStream(); ???????????????sUsed?=?s; ???????????}?catch?(IOException?e)?{ ???????????????//?TODO?Auto-generated?catch?block ???????????????e.printStackTrace(); ???????????} ???????} ???????@Override ???????public?void?run() ???????{ ?????????Log.d("service",?"socket?service?-?SocketReceiveThread::run"); ???????????while((!stop)?&&?(!mServerSocket.isClosed())) ???????????{ ???????????????this.buf?=?new?byte[2048]; ???????????????//?讀取輸入的數(shù)據(jù)(阻塞讀) ???????????????try?{ ???????????????????this.mInputStream.read(buf); ???????????????}?catch?(IOException?e1)?{ ???????????????????//?TODO?Auto-generated?catch?block ???????????????????e1.printStackTrace(); ???????????????} ???????????????//?字符編碼轉(zhuǎn)換 ???????????????try?{ ???????????????????this.str?=?new?String(this.buf,?"GB2312").trim(); ???????????????}?catch?(UnsupportedEncodingException?e)?{ ???????????????????//?TODO?Auto-generated?catch?block ???????????????????e.printStackTrace(); ???????????????} ???????????????Intent?sendIntent?=?new?Intent(SOCKER_RCV); ???????????????sendIntent.putExtra("action",?"RcvStr"); ???????????????sendIntent.putExtra("content",?this.str); ???????????????//?發(fā)送廣播,將被Activity組件中的BroadcastReceiver接收到 ???????????????sendBroadcast(sendIntent); ???????????} ???????} ???} }
在每個 Activity 中處理 SOCKER_RCV action,以響應(yīng) Socket 狀態(tài)的變化和接收到數(shù)據(jù)。
Service 與 Activity 之間通訊需要使用到廣播: Broadcast。
(1) 在 Activity 中定義全局的變量,如下:
public?static?final?String?SOCKER_ACTION?=?"com.jia.Socket.Control"; public?static?final?String?SOCKER_RCV?=?"com.jia.Socket.ReceiveStr"; SocketReceiver?socketReceiver;
(2) 在 Activity 的 onCreate 中注冊廣播和啟動 Socket Service,如下:
socketReceiver?=?new?SocketReceiver(); IntentFilter?socketIntentFilter?=?new?IntentFilter(); socketIntentFilter.addAction(SOCKER_RCV); registerReceiver(socketReceiver,socketIntentFilter); Intent?socketIntent?=?new?Intent(); socketIntent.setClass(MainActivity.this,?SocketService.class); startService(socketIntent);???????//?啟動??Socket?服務(wù)
(3) SocketReceiver 是繼承自 BroadcastReceiver 的類,實現(xiàn)如下:
public?class?SocketReceiver?extends?BroadcastReceiver?{ ??@Override ??public?void?onReceive(Context?context,?Intent?intent)?{ ????//?TODO?Auto-generated?method?stub ????String?action?=?intent.getAction(); ??????????if(action.equals(SOCKER_RCV))?{ ????????????String?url?=?intent.getExtras().getString("action"); ????????????if(url.equals("ClientIP"))?{ ??????????????String?strIP?=?intent.getExtras().getString("content"); ????????????} ????????????else?if(url.equals("RcvStr"))?{ ??????????????String?strContent?=?intent.getExtras().getString("content"); ????????????} ????????????else?if(url.equals("Disconnect"))?{ ??????????????String?strContent?=?intent.getExtras().getString("content"); ????????????} ????????} ????} }
(4) Socket 功能實現(xiàn)后,測試時發(fā)現(xiàn)客戶端(也就是 PC 端)斷開時手機端未檢測到 Socket 連接斷開。
以前使用 WinCE 時,Socket(TCP) 斷開時,無論是客戶端、還是服務(wù)器都可以檢測到 TCP 斷開的事件,并處理。但 Android 下的 Socket 編程機制竟然沒有這個東東。
一,測試時發(fā)現(xiàn)當 PC 端斷開后,手機端的服務(wù)程序在執(zhí)行到下面的代碼段時不會阻塞,且函數(shù)的返回值是: -1。
二,在網(wǎng)上查找發(fā)現(xiàn)這個問題是 Android 下 Socket 都有的問題,可以通過發(fā)心跳包來處理。
所以將下面這段代碼:
//?讀取輸入的數(shù)據(jù)(阻塞讀) try?{ ????this.mInputStream.read(buf); }?catch?(IOException?e1)?{ ????//?TODO?Auto-generated?catch?block ????e1.printStackTrace(); } 修改為如下的代碼:? try?{ ????int?length?=?this.mInputStream.read(buf); ????if(-1?==?length)?{ ??????try?{ ????????sUsed.sendUrgentData(0xff); ??????} ??????catch(Exception?ex)?{ ????????//?鏈接已斷開 ????????Log.v("service",?"disconnect!!!"); ????????stop?=?true; ????????if(null?!=?mServerSocket)?{ ??????????mServerSocket.close(); ????????} ????????Intent?sendIntent?=?new?Intent(SOCKER_RCV); ????????sendIntent.putExtra("action",?"Disconnect"); ????????sendIntent.putExtra("content",?"read?is?-1?&?Urgent?Exception!"); ????????sendBroadcast(sendIntent); ????????continue; ??????} ????} }?catch?(IOException?e1)?{ ????//?TODO?Auto-generated?catch?block ????e1.printStackTrace(); }