转载请以链接形式标明出处: 本文出自: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鬼才” 的对话:
代码语言:javascript复制在吗?
在!
你好
你好!
能听懂汉语吗?
能听懂汉语!
真的吗?
真的!
1.首先申明网络权限
代码语言:javascript复制<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鬼才聊天
代码语言:javascript复制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!
如果觉得不错的话,请帮忙点个赞呗。
以上
`