本文的合集已經(jīng)編著成書,高級Android開發(fā)強化實戰(zhàn)议慰,歡迎各位讀友的建議和指導(dǎo)搔预。在京東即可購買:https://item.jd.com/12385680.html
Socket是套接字, 網(wǎng)絡(luò)通信經(jīng)常使用的方法, 分為TCP和UDP兩種模式, 需要網(wǎng)絡(luò)權(quán)限, 當(dāng)然也可以應(yīng)用于跨進程通信. 本文通過一個簡易的Android聊天程序, 熟悉Socket的使用方法.
本文源碼的GitHub下載地址
邏輯: 客戶端向服務(wù)端發(fā)送數(shù)據(jù), 服務(wù)端收到后返回客戶端數(shù)據(jù).
Server
Socket處理屬于網(wǎng)絡(luò)請求, 需要在其他線程中使用, 不能應(yīng)用于主線程.
new Thread(new TcpServer()).start();
TCP服務(wù)的Socket鏈接. 設(shè)置Socket的端口號ServerSocket(PORT)
, 不斷循環(huán)的接收數(shù)據(jù)serverSocket.accept()
, 在responseClient()
方法處理數(shù)據(jù).
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, "接收數(shù)據(jù)");
new Thread() {
@Override public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
mIsServiceDestroyed
用于判斷服務(wù)器是否存活, 防止內(nèi)存泄露.
處理Socket數(shù)據(jù), 使用BufferedReader
讀取數(shù)據(jù), 使用PrintWriter
寫入數(shù)據(jù), 循環(huán)檢測, 結(jié)束時關(guān)閉緩存和Socket.
private void responseClient(Socket client) throws IOException {
// 接收客戶端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 向客戶端發(fā)送消息
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, "發(fā)送信息: " + msg);
}
System.out.println("客戶端退出");
// 關(guān)閉通信
close(out);
close(in);
client.close();
}
服務(wù)器使用單獨線程, 模擬跨進程通信.
<service
android:name=".TCPServerService"
android:process=":remote"/>
需要申請網(wǎng)絡(luò)權(quán)限, 連網(wǎng)和訪問網(wǎng)絡(luò)狀態(tài).
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Client
客戶端, 向服務(wù)器發(fā)送數(shù)據(jù), 并接收服務(wù)器返回的數(shù)據(jù).
啟動服務(wù), 連接TCP服務(wù)器.
Intent intent = new Intent(this, TCPServerService.class);
startService(intent);
new Thread(new Runnable() {
@Override public void run() {
connectTCPServer();
}
}).start();
嘗試連接服務(wù)器, 每隔1秒進行重試, 并初始化發(fā)送緩存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, "服務(wù)器連接成功");
} catch (IOException e) {
SystemClock.sleep(1000);
Log.e(TAG, "連接TCP服務(wù)失敗, 重試...");
}
}
成功后, 循環(huán)調(diào)用, 監(jiān)聽BufferedReader
, 是否有數(shù)據(jù)返回.
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處理數(shù)據(jù), 分為連接成功和獲取數(shù)據(jù)兩種情況.
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;
}
}
};
點擊按鈕發(fā)送數(shù)據(jù), 直接在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));
}
}
當(dāng)我們向服務(wù)端發(fā)送數(shù)據(jù)時, 就會獲取服務(wù)端的返回, 模擬聊天效果.
效果
Socket作為經(jīng)典的網(wǎng)絡(luò)通信方式, 有很多應(yīng)用, 也可以實現(xiàn)跨進程通信, 希望能熟練掌握.
OK, that's all! Enjoy it!