本文的合集已經編著成書,高級Android開發強化實戰,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html
Socket是套接字, 網絡通信經常使用的方法, 分爲TCP和UDP兩種模式, 需要網絡權限, 當然也可以應用於跨進程通信. 本文通過一個簡易的Android聊天程序, 熟悉Socket的使用方法.
本文源碼的GitHub下載地址
邏輯: 客戶端向服務端發送數據, 服務端收到後返回客戶端數據.
Server
Socket處理屬於網絡請求, 需要在其他線程中使用, 不能應用於主線程.
new Thread(new TcpServer()).start();
TCP服務的Socket鏈接. 設置Socket的端口號ServerSocket(PORT)
, 不斷循環的接收數據serverSocket.accept()
, 在responseClient()
方法處理數據.
private class TcpServer implements Runnable {
@Override public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(PORT);
} catch (IOException e) {
Log.e(TAG, "建立鏈接失敗, 端口:" + PORT);
e.printStackTrace();
return; // 鏈接建立失敗直接返回
}
while (!mIsServiceDestroyed) {
try {
final Socket client = serverSocket.accept();
Log.e(TAG, "接收數據");
new Thread() {
@Override public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
mIsServiceDestroyed
用於判斷服務器是否存活, 防止內存泄露.
處理Socket數據, 使用BufferedReader
讀取數據, 使用PrintWriter
寫入數據, 循環檢測, 結束時關閉緩存和Socket.
private void responseClient(Socket client) throws IOException {
// 接收客戶端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 向客戶端發送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("歡迎歡迎, 我是Spike!");
while (!mIsServiceDestroyed) {
String str = in.readLine();
Log.e(TAG, "信息來自: " + str);
if (str == null) {
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.println(msg);
Log.e(TAG, "發送信息: " + msg);
}
System.out.println("客戶端退出");
// 關閉通信
close(out);
close(in);
client.close();
}
服務器使用單獨線程, 模擬跨進程通信.
<service
android:name=".TCPServerService"
android:process=":remote"/>
需要申請網絡權限, 連網和訪問網絡狀態.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Client
客戶端, 向服務器發送數據, 並接收服務器返回的數據.
啓動服務, 連接TCP服務器.
Intent intent = new Intent(this, TCPServerService.class);
startService(intent);
new Thread(new Runnable() {
@Override public void run() {
connectTCPServer();
}
}).start();
嘗試連接服務器, 每隔1秒進行重試, 並初始化發送緩存PrintWriter
.
Socket socket = null;
// 不停重試直到連接成功爲止
while (socket == null) {
try {
socket = new Socket("localhost", TCPServerService.PORT);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
Log.e(TAG, "服務器連接成功");
} catch (IOException e) {
SystemClock.sleep(1000);
Log.e(TAG, "連接TCP服務失敗, 重試...");
}
}
成功後, 循環調用, 監聽BufferedReader
, 是否有數據返回.
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
while (!MainActivity.this.isFinishing()) {
String msg = br.readLine();
Log.e(TAG, "收到信息: " + msg);
if (msg != null) {
String time = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH).format(System.currentTimeMillis());
String showedMsg = "server " + time + ":" + msg + "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg)
.sendToTarget();
}
}
Handler處理數據, 分爲連接成功和獲取數據兩種情況.
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG:
mTvContent.setText(
String.valueOf(mTvContent.getText().toString() + msg.obj));
break;
case MESSAGE_SOCKET_CONNECTED:
mBSend.setEnabled(true);
break;
default:
break;
}
}
};
點擊按鈕發送數據, 直接在PrintWriter中寫入, 即可.
public void sendMessage(View view) {
String msg = mEtMessage.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
mPrintWriter.println(msg);
mEtMessage.setText("");
String time = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH).format(System.currentTimeMillis());
String showedMsg = "self " + time + ":" + msg + "\n";
mTvContent.setText(String.valueOf(mTvContent.getText() + showedMsg));
}
}
當我們向服務端發送數據時, 就會獲取服務端的返回, 模擬聊天效果.
效果
Socket作爲經典的網絡通信方式, 有很多應用, 也可以實現跨進程通信, 希望能熟練掌握.
OK, that’s all! Enjoy it!