編輯:關於Android編程
Android 通過Socket 和服務器通訊,是一種比較常用的通訊方式,時間比較緊,說下大致的思路,希望能幫到使用socket 進行通信的人
(1)開啟一個線程發送消息 SocketOutputThread
消息是放在隊列裡的,當有消息後,進入隊列,線程喚醒,發送消息,並反饋發送是否成功的回調
(2)開啟一個線程接受服務器消息 SocketInputThread
為了防止一直收數據,浪費電池的電,采用NIO的方式讀socket的數據,這個是本文的關鍵
(3)開啟一個線程,做心跳,防止socket連接終斷 , SocketHeartThread
(4)構建 SocketThreadManager對以上三個thread進行管理
(5)構建 TCPClient 發送socket消息
在NIO的方式實現TCP,特別是在接收服務器的數據,不用寫個線程定時去讀了。
DEMO 截圖
主要代碼如下,詳細代碼在附件裡。
SocketOutPutThread 類
package com.example.socketblockdemo; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import android.os.Bundle; import android.os.Handler; import android.os.Message; /** * 客戶端寫消息線程 * * @author way * */ public class SocketOutputThread extends Thread { private boolean isStart = true; private static String tag = "socketOutputThread"; private List<MsgEntity> sendMsgList; public SocketOutputThread( ) { sendMsgList = new CopyOnWriteArrayList<MsgEntity>(); } public void setStart(boolean isStart) { this.isStart = isStart; synchronized (this) { notify(); } } // 使用socket發送消息 public boolean sendMsg(byte[] msg) throws Exception { if (msg == null) { CLog.e(tag, "sendMsg is null"); return false; } try { TCPClient.instance().sendMsg(msg); } catch (Exception e) { throw (e); } return true; } // 使用socket發送消息 public void addMsgToSendList(MsgEntity msg) { synchronized (this) { this.sendMsgList.add(msg); notify(); } } @Override public void run() { while (isStart) { // 鎖發送list synchronized (sendMsgList) { // 發送消息 for (MsgEntity msg : sendMsgList) { Handler handler = msg.getHandler(); try { sendMsg(msg.getBytes()); sendMsgList.remove(msg); // 成功消息,通過hander回傳 if (handler != null) { Message message = new Message(); message.obj = msg.getBytes(); message.what =1; handler.sendMessage(message); // handler.sendEmptyMessage(1); } } catch (Exception e) { e.printStackTrace(); CLog.e(tag, e.toString()); // 錯誤消息,通過hander回傳 if (handler != null) { Message message = new Message(); message.obj = msg.getBytes(); message.what = 0;; handler.sendMessage(message); } } } } synchronized (this) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }// 發送完消息後,線程進入等待狀態 } } } }
SocketInputThread
package com.example.socketblockdemo; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import android.content.Intent; import android.text.TextUtils; /** * 客戶端讀消息線程 * * @author way * */ public class SocketInputThread extends Thread { private boolean isStart = true; private static String tag = "socket"; // private MessageListener messageListener;// 消息監聽接口對象 public SocketInputThread() { } public void setStart(boolean isStart) { this.isStart = isStart; } @Override public void run() { while (isStart) { // 手機能聯網,讀socket數據 if (NetManager.instance().isNetworkConnected()) { if (!TCPClient.instance().isConnect()) { CLog.e(tag, "TCPClient connet server is fail read thread sleep second" +Const.SOCKET_SLEEP_SECOND ); try { sleep(Const.SOCKET_SLEEP_SECOND * 1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } readSocket(); // 如果連接服務器失敗,服務器連接失敗,sleep固定的時間,能聯網,就不需要sleep CLog.e("socket","TCPClient.instance().isConnect() " + TCPClient.instance().isConnect() ); } } } public void readSocket() { Selector selector = TCPClient.instance().getSelector(); if (selector == null) { return; } try { // 如果沒有數據過來,一直柱塞 while (selector.select() > 0) { for (SelectionKey sk : selector.selectedKeys()) { // 如果該SelectionKey對應的Channel中有可讀的數據 if (sk.isReadable()) { // 使用NIO讀取Channel中的數據 SocketChannel sc = (SocketChannel) sk.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); try { sc.read(buffer); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); // continue; } buffer.flip(); String receivedString = ""; // 打印收到的數據 try { receivedString = Charset.forName("UTF-8") .newDecoder().decode(buffer).toString(); CLog.e(tag, receivedString); Intent i = new Intent(Const.BC); i.putExtra("response", receivedString); MainActivity.s_context.sendBroadcast(i ); } catch (CharacterCodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } buffer.clear(); buffer = null; try { // 為下一次讀取作准備 sk.interestOps(SelectionKey.OP_READ); // 刪除正在處理的SelectionKey selector.selectedKeys().remove(sk); } catch (CancelledKeyException e) { e.printStackTrace(); } } } } // selector.close(); // TCPClient.instance().repareRead(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (ClosedSelectorException e2) { } } }
SocketHeartHread 心態類
package com.example.socketblockdemo; import java.io.IOException; import android.text.TextUtils; class SocketHeartThread extends Thread { boolean isStop = false; boolean mIsConnectSocketSuccess = false; static SocketHeartThread s_instance; private TCPClient mTcpClient = null; static final String tag = "SocketHeartThread"; public static synchronized SocketHeartThread instance() { if (s_instance == null) { s_instance = new SocketHeartThread(); } return s_instance; } public SocketHeartThread() { TCPClient.instance(); // 連接服務器 // mIsConnectSocketSuccess = connect(); } public void stopThread() { isStop = true; } /** * 連接socket到服務器, 並發送初始化的Socket信息 * * @return */ private boolean reConnect() { return TCPClient.instance().reConnect(); } public void run() { isStop = false; while (!isStop) { // 發送一個心跳包看服務器是否正常 boolean canConnectToServer = TCPClient.instance().canConnectToServer(); if(canConnectToServer == false){ reConnect(); } try { Thread.sleep(Const.SOCKET_HEART_SECOND * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
線程管理類
package com.example.socketblockdemo; import android.os.Handler; import android.text.TextUtils; public class SocketThreadManager { private static SocketThreadManager s_SocketManager = null; private SocketInputThread mInputThread = null; private SocketOutputThread mOutThread = null; private SocketHeartThread mHeartThread = null; // 獲取單例 public static SocketThreadManager sharedInstance() { if (s_SocketManager == null) { s_SocketManager = new SocketThreadManager(); s_SocketManager.startThreads(); } return s_SocketManager; } // 單例,不允許在外部構建對象 private SocketThreadManager() { mHeartThread = new SocketHeartThread(); mInputThread = new SocketInputThread(); mOutThread = new SocketOutputThread(); } /** * 啟動線程 */ private void startThreads() { mHeartThread.start(); mInputThread.start(); mInputThread.setStart(true); mOutThread.start(); mInputThread.setStart(true); // mDnsthread.start(); } /** * stop線程 */ public void stopThreads() { mHeartThread.stopThread(); mInputThread.setStart(false); mOutThread.setStart(false); } public static void releaseInstance() { if (s_SocketManager != null) { s_SocketManager.stopThreads(); s_SocketManager = null; } } public void sendMsg(byte [] buffer, Handler handler) { MsgEntity entity = new MsgEntity(buffer, handler); mOutThread.addMsgToSendList(entity); } }
TCPClient ,采用NIO的方式構建
package com.example.socketblockdemo; import java.io.IOException; import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; /** * NIO TCP 客戶端 * */ public class TCPClient { // 信道選擇器 private Selector selector; // 與服務器通信的信道 SocketChannel socketChannel; // 要連接的服務器Ip地址 private String hostIp; // 要連接的遠程服務器在監聽的端口 private int hostListenningPort; private static TCPClient s_Tcp = null; public boolean isInitialized = false; public static synchronized TCPClient instance() { if (s_Tcp == null) { s_Tcp = new TCPClient(Const.SOCKET_SERVER, Const.SOCKET_PORT); } return s_Tcp; } /** * 構造函數 * * @param HostIp * @param HostListenningPort * @throws IOException */ public TCPClient(String HostIp, int HostListenningPort) { this.hostIp = HostIp; this.hostListenningPort = HostListenningPort; try { initialize(); this.isInitialized = true; } catch (IOException e) { this.isInitialized = false; // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { this.isInitialized = false; e.printStackTrace(); } } /** * 初始化 * * @throws IOException */ public void initialize() throws IOException { boolean done = false; try { // 打開監聽信道並設置為非阻塞模式 socketChannel = SocketChannel.open(new InetSocketAddress(hostIp, hostListenningPort)); if (socketChannel != null) { socketChannel.socket().setTcpNoDelay(false); socketChannel.socket().setKeepAlive(true); // 設置 讀socket的timeout時間 socketChannel.socket().setSoTimeout( Const.SOCKET_READ_TIMOUT); socketChannel.configureBlocking(false); // 打開並注冊選擇器到信道 selector = Selector.open(); if (selector != null) { socketChannel.register(selector, SelectionKey.OP_READ); done = true; } } } finally { if (!done && selector != null) { selector.close(); } if (!done) { socketChannel.close(); } } } static void blockUntil(SelectionKey key, long timeout) throws IOException { int nkeys = 0; if (timeout > 0) { nkeys = key.selector().select(timeout); } else if (timeout == 0) { nkeys = key.selector().selectNow(); } if (nkeys == 0) { throw new SocketTimeoutException(); } } /** * 發送字符串到服務器 * * @param message * @throws IOException */ public void sendMsg(String message) throws IOException { ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("utf-8")); if (socketChannel == null) { throw new IOException(); } socketChannel.write(writeBuffer); } /** * 發送數據 * * @param bytes * @throws IOException */ public void sendMsg(byte[] bytes) throws IOException { ByteBuffer writeBuffer = ByteBuffer.wrap(bytes); if (socketChannel == null) { throw new IOException(); } socketChannel.write(writeBuffer); } /** * * @return */ public synchronized Selector getSelector() { return this.selector; } /** * Socket連接是否是正常的 * * @return */ public boolean isConnect() { boolean isConnect = false; if (this.isInitialized) { isConnect = this.socketChannel.isConnected(); } return isConnect; } /** * 關閉socket 重新連接 * * @return */ public boolean reConnect() { closeTCPSocket(); try { initialize(); isInitialized = true; } catch (IOException e) { isInitialized = false; e.printStackTrace(); } catch (Exception e) { isInitialized = false; e.printStackTrace(); } return isInitialized; } /** * 服務器是否關閉,通過發送一個socket信息 * * @return */ public boolean canConnectToServer() { try { if (socketChannel != null) { socketChannel.socket().sendUrgentData(0xff); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } catch (Exception e){ e.printStackTrace(); return false; } return true; } /** * 關閉socket */ public void closeTCPSocket() { try { if (socketChannel != null) { socketChannel.close(); } } catch (IOException e) { } try { if (selector != null) { selector.close(); } } catch (IOException e) { } } /** * 每次讀完數據後,需要重新注冊selector,讀取數據 */ public synchronized void repareRead() { if (socketChannel != null) { try { selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_READ); } catch (ClosedChannelException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } }
如何使用
// 發送消息,失敗或者成功的handler SocketThreadManager.sharedInstance().sendMsg(str.getBytes(), handler);
代碼下載:demo
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
先給大家展示下效果圖:package com.lixu.circlemenu; import android.app.Activity; import android.o
使用命令行創建項目cordova 創建項目的命令是cordova create 第一個參數 < dir >:項目目錄文件夾的名稱第二個參數< packa
上幾節,我們學習如何用StartServer啟動一個服務,用bindServer去綁定一個服務,以及服務的生命周期,以及什麼是IntentService。或許有讀者會發現
Android Studio,自Google2013年發布以來,就倍受Android開發者的喜愛,我們本書,就是基於Android Studio來進行案例演示的,大家都