轉載請以鏈接形式標明出處:
本文出自:103style的博客
《Android開發藝術探索》 學習記錄
base on AndroidStudio 3.5.1
目錄
- 前言
- 實現跨進程與AI鬼才聊天
- 小結
簡介
前面我們介紹了:
進程間通信基礎介紹
通過AIDL介紹Binder的工作機制
通過 Bundle、文件共享、Messenger實現進程間通信
進程間通信的方式之AIDL
進程間通信的方式之ContentProvider
本文主要介紹進程間通信的方式之 Scoket。
Socket 也稱之爲 “套接字”,是網絡通信中的概念,分 流式套接字 和 用戶數據套接字,分別對應網絡的傳輸控制層中的 TCP 和 UDP 協議。
TCP是面向連接的協議,提供雙向通信功能,連接建立需要 “三次握手”,爲了傳輸的穩定性,其本身提供了超時機制。
UDP是無連接的,提供不穩定的單向通信功能,也可以提供雙向。
在性能上UDP有更好的效率,缺點就是不能保證數據正確傳輸,尤其是網絡擁塞的情況下。
下面我們來演示一個通過Socket實現跨進程聊天的程序,Socket本身可以支持傳輸任意的字節流,我們這裏僅傳輸文本信息。
實現跨進程與AI鬼才聊天
主要由 服務端 和 客戶端組成,在服務端創建一個 TCP服務, 然後在客戶端連接這個服務,然後開始相互聊天,來模仿一下“AI鬼才” 的對話:
在嗎?
在!
你好
你好!
能聽懂漢語嗎?
能聽懂漢語!
真的嗎?
真的!
1.首先申明網絡權限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
然後注意一點在Android4.0 以上不能直接在UI線程進行網絡訪問。
2.實現服務端代碼
步驟大致爲:
- 創建一個ServerSocket,響應客戶端的連接,注意服務端和客戶端爲一對多關係。
(TCPServer)
- 處理“AI鬼才”聊天的邏輯.
(responseClient(...))
public class TCPServerService extends Service {
public static int PORT = 10024;
private static final String TAG = "TCPServerService";
private boolean destroyed = false;
public void onCreate() {
new Thread(new TCPServer()).start();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
destroyed = true;
super.onDestroy();
}
private void responseClient(Socket client) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
pw.println("welcome to chat room!");
while (!destroyed) {
String s = br.readLine();
if (TextUtils.isEmpty(s)) {
SystemClock.sleep(1000);
continue;
}
Log.e(TAG, "client send msg = " + s);
//AI鬼才邏輯
if (s.contains("嗎?")) {
s = s.replaceAll("嗎?", "!");
} else {
char end = s.charAt(s.length() - 1);
if (end < '0' || (end > '9' && end < 'A')) {
s = s.substring(0, s.length() - 1) + "!";
} else {
s += "!";
}
}
if (s.contains("你")) {
s = s.replaceAll("你", "我");
}
pw.println(s);
}
pw.close();
br.close();
client.close();
}
private class TCPServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
while (serverSocket == null) {
try {
serverSocket = new ServerSocket(PORT);
} catch (IOException e) {
Log.e(TAG, "establish tcp server failed, port =" + PORT);
PORT++;
SystemClock.sleep(1000);
Log.e(TAG, "retry...");
}
}
while (!destroyed) {
try {
Socket client = serverSocket.accept();
Log.e(TAG, "erverSocket accepted");
new Thread(() -> {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} catch (IOException e) {
Log.e(TAG, "serverSocket.accept error");
SystemClock.sleep(1000);
}
}
}
}
}
在 AndroidManifest.xml
中聲明,配置爲 :remote 進程。
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ... >
<service
android:name="socket.TCPServerService"
android:process=":remote" />
</application>
</manifest>
3.實現客戶端代碼
步驟爲:
- 啓動服務端服務
startService(new Intent(this, TCPServerService.class))
- 初始化Socket,然後定時去讀服務端發的消息,注意要在非UI線程中初始化Socket:
init()
- 然後再界面上添加一個文本編輯和發送,注意發送要在非UI線程中執行:
sendMsg()
public class SocketTestActivity extends AppCompatActivity {
private static final String TAG = "SocketTestActivity";
private Socket mClientSocket;
private PrintWriter mPrintWriter;
private EditText editText;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
editText = findViewById(R.id.input);
startService(new Intent(this, TCPServerService.class));
init();
findViewById(R.id.send).setOnClickListener(v -> new Thread(() -> sendMsg()).start());
}
private void init() {
new Thread(() -> {
try {
initSocket();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private void initSocket() throws IOException {
Socket socket = null;
while (socket == null) {
try {
socket = new Socket("localhost", TCPServerService.PORT);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mClientSocket.getOutputStream())), true);
Log.e(TAG, "connect server success");
} catch (IOException e) {
SystemClock.sleep(1000);
Log.e(TAG, "initSocket: connect fail, retry....");
}
}
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!SocketTestActivity.this.isFinishing()) {
String msg = bufferedReader.readLine();
if (TextUtils.isEmpty(msg)) {
SystemClock.sleep(1000);
continue;
}
Log.e(TAG, "receive msg from server = " + msg);
}
Log.e(TAG, "chat quit");
mPrintWriter.close();
bufferedReader.close();
}
private void sendMsg() {
String temp = editText.getText().toString();
if (TextUtils.isEmpty(temp) || mPrintWriter == null) {
return;
}
mPrintWriter.println(temp);
editText.setText("");
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mPrintWriter != null) {
mPrintWriter.close();
}
super.onDestroy();
}
}
4.啓動程序,開始和 AI鬼才聊天
receive msg from server = welcome to chat room!
client send msg = 123
receive msg from server = 123!
client send msg = Hi
receive msg from server = Hi!
client send msg = understand?
receive msg from server = understand?!
client send msg = Really?
receive msg from server = Really?!
client send msg = NICE
receive msg from server = NICE!
如果覺得不錯的話,請幫忙點個讚唄。
以上
掃描下面的二維碼,關注我的公衆號 Android1024, 點關注,不迷路。
`