当前位置: 首页 > news >正文

免费企业网站开源系统广州 网站开发 骏域

免费企业网站开源系统,广州 网站开发 骏域,重庆互联网网站建设,福田区网站建手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解…手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解读 ServerReader类代码解读 **客户端**Android:结构预览布局文件 activity_main.xml代码解读 MainActivity代码解读 配置网络配置网络 基本实现 实现客户端和服务端之间的通信 实现服务端转接客户端消息,并发送给其他局域网在线成员 实现服务端接收客户端消息,并判断相应类型,做出对应应答 实现客户端消息发送者 发送时间 当前在线用户基本可视化 问题分析 服务端开发 在IntelliJ IDEA中创建一个Java项目。 实现一个简单的TCP服务器能够接收客户端消息并回显或广播消息给所有已连接的客户端。 客户端开发 在Android Studio中创建一个Android项目。实现一个简单的TCP客户端能够发送消息到服务端并显示从服务端接收到的消息。 网络通信 确保服务端和客户端在同一局域网内并且客户端可以正确连接到服务端。处理多线程问题确保服务端可以同时处理多个客户端连接。 服务端 Idea: 结构预览 在Idea中创建一个名为Server的类 Server类 import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map;public class Server {// 定义一个集合容器存储所有登陆进来的客户端,以便群发消息给他们// 定义一个Map集合,键是存储客户端的管道,值是这个管道的名称public static final MapSocket, String onLineSockets new HashMap();public static void main(String[] args) throws Exception {System.out.println(服务端启动);// 1. 创建服务端ServerSocket对象,绑定端口号,监听客户端连接ServerSocket serverSocket new ServerSocket(9999);while (true) {System.out.println(等待客户端连接....);Socket socket serverSocket.accept();new ServerReader(socket).start();System.out.println(一个客户端上线了.... IP: socket.getInetAddress().getHostAddress());}} }代码解读 定义一个Map集合(所有局域网用户共享集合)存储所有登陆进来的客户端,以便群发消息给他们,键是存储客户端的管道,值是这个管道的名称(说白了就是前一个是主键,后一个) public static final MapSocket, String onLineSockets new HashMap();创建服务端ServerSocket对象,绑定端口号,监听客户端连接 ServerSocket serverSocket new ServerSocket(9999);端口号选择建议范围**(1024~65535)**,其中绝大多数没有被使用 while (true) {System.out.println(等待客户端连接....);Socket socket serverSocket.accept();new ServerReader(socket).start();System.out.println(一个客户端上线了.... IP: socket.getInetAddress().getHostAddress());}使用无限循环,持续监听新的连接 **serverSocket.accept()**会堵塞线程,等待连接请求,直到收到一个新的请求,并与客户端建立新的通信管道用来传输数据 **new ServerReader(socket).start()**在建立新的管道后,会建立一个新线程用来与管道对应的客户端通信,这样就能实现多客户端之间通信 **socket.getInetAddress().getHostAddress()**用于获取新建连接的客户端Ip,并在Server类终端打印,便于服务端查看连接信息 在Idea中创建一个名为ServerReader的类 注:本文所有读取和发送都使用特殊流DataInputStream与DataOutputStream ServerReader类 import java.io.*; import java.net.Socket; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Collection;public class ServerReader extends Thread {private Socket socket;public ServerReader(Socket socket) {this.socket socket;}Overridepublic void run() {try {DataInputStream dis new DataInputStream(socket.getInputStream());while (true) {int type dis.readInt(); // 1 2switch (type) {case 1:String nickname dis.readUTF();// 登陆成功,将客户端socket存入在线集合Server.onLineSockets.put(socket, nickname);// 更新全部客户端的在线人数列表updateClientOnLineUserList();break;case 2:String msg dis.readUTF();sendMsgToAll(msg);break;default:System.out.println(未知的消息类型: type);break;}}} catch (Exception e) {System.out.println(客户端断开连接 IP: socket.getInetAddress().getHostAddress() 时间: LocalDateTime.now());Server.onLineSockets.remove(socket); // 把下线的客户端socket从在线集合中移除updateClientOnLineUserList();}}// 给全部在线socket推送当前客户端发来的消息private void sendMsgToAll(String msg) {StringBuilder sb new StringBuilder();String name Server.onLineSockets.get(socket);// 获取当前时间LocalDateTime now LocalDateTime.now();DateTimeFormatter dft DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss EEE a);String nowStr dft.format(now);String msgResult sb.append(name).append( ).append(nowStr).append(\r\n).append(msg).append(\r\n).toString();// 推送给全部客户端for (Socket clientSocket : Server.onLineSockets.keySet()) {try {DataOutputStream dos new DataOutputStream(clientSocket.getOutputStream());dos.writeInt(2); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息dos.writeUTF(msgResult);dos.flush();} catch (IOException e) {e.printStackTrace();}}}// 更新全部客户端的在线人数列表private void updateClientOnLineUserList() {// 拿到全部在线客户端的用户名称,把这些名称转发给全部在线socket管道CollectionString onLineUsers Server.onLineSockets.values();for (Socket clientSocket : Server.onLineSockets.keySet()) {try {DataOutputStream dos new DataOutputStream(clientSocket.getOutputStream());dos.writeInt(1); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息dos.writeInt(onLineUsers.size());for (String onLineUser : onLineUsers) {dos.writeUTF(onLineUser);}dos.flush();} catch (IOException e) {e.printStackTrace();}}} }代码解读 sendMsgToAll方法 // 给全部在线socket推送当前客户端发来的消息private void sendMsgToAll(String msg) {StringBuilder sb new StringBuilder();String name Server.onLineSockets.get(socket);// 获取当前时间LocalDateTime now LocalDateTime.now();DateTimeFormatter dft DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss EEE a);String nowStr dft.format(now);String msgResult sb.append(name).append( ).append(nowStr).append(\r\n).append(msg).append(\r\n).toString();// 推送给全部客户端for (Socket clientSocket : Server.onLineSockets.keySet()) {try {DataOutputStream dos new DataOutputStream(clientSocket.getOutputStream());dos.writeInt(2); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息dos.writeUTF(msgResult);dos.flush();} catch (IOException e) {e.printStackTrace();}}}从Map集合(onLineSockets)中,拿到当前客户端的用户名 String name Server.onLineSockets.get(socket);获取当前时间,自定义时间格式dft yyyy-MM-dd HH:mm:ss EEE a年 月 日 时 分 秒 星期 上下午 LocalDateTime now LocalDateTime.now();DateTimeFormatter dft DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss EEE a);String nowStr dft.format(now);拼装消息 用户名空格时间换行回车消息换行回车 String msgResult sb.append(name).append( ).append(nowStr).append(\r\n).append(msg).append(\r\n).toString();将拼装完成的消息,推送给所有当前在线客户端 for循环遍历当前在线客户端 标注消息类型为群聊消息(2) 接着发送拼装完成的消息 刷新管道 for (Socket clientSocket : Server.onLineSockets.keySet()) {try {DataOutputStream dos new DataOutputStream(clientSocket.getOutputStream());dos.writeInt(2);dos.writeUTF(msgResult);dos.flush();} catch (IOException e) {e.printStackTrace();}}updateClientOnLineUserList方法 // 更新全部客户端的在线人数列表private void updateClientOnLineUserList() {// 拿到全部在线客户端的用户名称,把这些名称转发给全部在线socket管道CollectionString onLineUsers Server.onLineSockets.values();for (Socket clientSocket : Server.onLineSockets.keySet()) {try {DataOutputStream dos new DataOutputStream(clientSocket.getOutputStream());dos.writeInt(1); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息dos.writeInt(onLineUsers.size());for (String onLineUser : onLineUsers) {dos.writeUTF(onLineUser);}dos.flush();} catch (IOException e) {e.printStackTrace();}}}拿到全部在线客户端的用户名称,把这些名称转发给全部在线socket管道 CollectionString onLineUsers Server.onLineSockets.values();1代表消息类型 告诉客户端接下来是在线人数列表信息 dos.writeInt(1);告诉客户端在线用户数量,客户端循环接收多少次 dos.writeInt(onLineUsers.size());服务端循环发送 for (String onLineUser : onLineUsers) {dos.writeUTF(onLineUser);}刷新管道 dos.flush();run方法 public void run() {try {DataInputStream dis new DataInputStream(socket.getInputStream());while (true) {int type dis.readInt(); // 1 2switch (type) {case 1:String nickname dis.readUTF();// 登陆成功,将客户端socket存入在线集合Server.onLineSockets.put(socket, nickname);// 更新全部客户端的在线人数列表updateClientOnLineUserList();break;case 2:String msg dis.readUTF();sendMsgToAll(msg);break;default:System.out.println(未知的消息类型: type);break;}}} catch (Exception e) {System.out.println(客户端断开连接 IP: socket.getInetAddress().getHostAddress() 时间: LocalDateTime.now());Server.onLineSockets.remove(socket); // 把下线的客户端socket从在线集合中移除updateClientOnLineUserList();}}创建一个读取socket管道的对象dis DataInputStream dis new DataInputStream(socket.getInputStream());while循环保证该方法一直处于接收消息的状态 先获取管道中先发送的数据类型 int type dis.readInt();switch (type)判断: 如果是1,则为用户名,String nickname dis.readUTF()读取管道中后发送的内容(用户名),这个时候用户已经登录成功,用Server.onLineSockets.put(socket, nickname)将客户端socket存入在线集合onLineSockets(该集合在前面已经创建在Server类里了) 如果是2,则为群聊消息,String msg dis.readUTF()读取管道中的后发送的群聊消息,接着调用sendMsgToAll()方法将消息广播给在线用户 如果使用的传输数据的方式不是特殊流,则打印出该消息在特殊流下的形式(可能是一堆乱码) 异常:当客户端断开连接后,系统会抛出一个异常,用Server.onLineSockets.remove(socket) 把下线的客户端socket从在线集合中移除,重新调用updateClientOnLineUserList()方法,刷新在线用户列表 catch (Exception e) {System.out.println(客户端断开连接 IP: socket.getInetAddress().getHostAddress() 时间: LocalDateTime.now());Server.onLineSockets.remove(socket);updateClientOnLineUserList();}客户端 Android: 创建一个空项目Client 因为安卓客户端只有一个Activity和一个布局文件,所以项目构建完成后就不需要再创建其他类和活动了 结构预览 布局文件 activity_main.xml 预览 ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:orientationverticalandroid:padding16dpTextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text聊天室android:textSize24spandroid:textStyleboldandroid:gravitycenterandroid:layout_gravitycenter_horizontalandroid:layout_marginBottom16dp /EditTextandroid:idid/input_fieldandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:hintType a message...android:inputTypetextMultiLineandroid:minLines3android:maxLines5 /Buttonandroid:idid/send_buttonandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text发送android:layout_gravityendandroid:layout_marginTop8dp /ListViewandroid:idid/message_list_viewandroid:layout_widthmatch_parentandroid:layout_height0dpandroid:layout_weight1android:dividerHeight1dpandroid:layout_marginTop16dp /TextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text在线用户列表android:textSize18spandroid:textStyleboldandroid:layout_marginTop16dp /ListViewandroid:idid/user_list_viewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:dividerHeight1dpandroid:layout_marginTop8dp / /LinearLayout代码解读 涉及到的布局属性 xml声明,编码方式为utf-8 ?xml version1.0 encodingutf-8?开始一个线性布局容器 LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android宽高和父本容器同尺寸(全屏) android:layout_widthmatch_parent android:layout_heightmatch_parent线性布局方向为垂直方向 android:orientationvertical //horizontal水平从左向右排列,vertical垂直从上向下排列给整个布局设置一个16dp的内边距 android:padding16dp添加文本内容 android:textXXX文本大小 文本样式 对齐方式 android:textSize24sp //大小 android:textStylebold //样式 加粗 android:gravitycenter //对齐方式 居中让添加该属性的控件水平居中 android:layout_gravitycenter_horizontal文本输入框,输入提示:没输入内容时显示提示语句,输入文本后就不可见,起提示作用 android:hint//提示语句允许该控件输入框输入多行文本, android:inputTypetextMultiLine //允许输入多行文本 android:minLines3 //最少3行 android:maxLines5 //最多5行控件靠右 android:layout_gravityend这里说下android:gravity和android:layout_gravity区别 android:gravity作用对象为当前控件内部,比如有一个TextView的文本内容,如果使用android:gravitycenter,则会让文本内容在该TextView内部居中,和TextView在整个屏幕的位置没关系 android:layout_gravity作用对象为当前控件,这里还用TextView举例,如果使用android:layout_gravitycenter_horizontal,则会让该TextView在屏幕中的位置处于居中状态,和控件内部的内容没关系 ListView列表项之间的分割线高度 android:dividerHeight1dp控件间的距离 android:layout_marginTop16dp//当前控件与上方相邻控件的距离MainActivity import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import androidx.appcompat.app.AppCompatActivity; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import java.util.List;public class MainActivity extends AppCompatActivity {private Socket socket;private DataInputStream in;private DataOutputStream out;private Handler handler new Handler(Looper.getMainLooper());private ListString messages new ArrayList();private ArrayAdapterString messageAdapter;private ListString onlineUsers new ArrayList();private ArrayAdapterString userAdapter;private EditText inputField;private Button sendButton;private ListView messageListView, userListView;private String nickname XXX; // 使用实际的昵称Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);messageListView findViewById(R.id.message_list_view);userListView findViewById(R.id.user_list_view);inputField findViewById(R.id.input_field);sendButton findViewById(R.id.send_button);messageAdapter new ArrayAdapter(this, android.R.layout.simple_list_item_1, messages);messageListView.setAdapter(messageAdapter);userAdapter new ArrayAdapter(this, android.R.layout.simple_list_item_1, onlineUsers);userListView.setAdapter(userAdapter);sendButton.setOnClickListener(v - sendMessage());// 连接到服务器connectToServer();}private void connectToServer() {new Thread(() - {try {// 创建Socket对象并连接到服务器socket new Socket(192.168.68.206, 9999); // 替换为实际服务器IP和端口in new DataInputStream(socket.getInputStream());out new DataOutputStream(socket.getOutputStream());// 发送登录请求out.writeInt(1);out.writeUTF(nickname);// 开始接收消息receiveMessages();} catch (IOException e) {e.printStackTrace();handler.post(this::disconnect);}}).start();}private void sendMessage() {String message inputField.getText().toString().trim();if (!message.isEmpty()) {new Thread(() - {try {out.writeInt(2);out.writeUTF(message);inputField.setText();} catch (IOException e) {e.printStackTrace();handler.post(this::disconnect);}}).start();}}private void receiveMessages() {new Thread(() - {try {while (true) {int type in.readInt();switch (type) {case 1:int count in.readInt();for (int i 0; i count; i) {String user in.readUTF();updateOnlineUsers(user);}break;case 2:String msg in.readUTF();updateMessage(msg);break;}}} catch (IOException e) {e.printStackTrace();handler.post(this::disconnect);}}).start();}private void updateMessage(String message) {handler.post(() - {messages.add(message);messageAdapter.notifyDataSetChanged();});}private void updateOnlineUsers(String user) {handler.post(() - {if (!onlineUsers.contains(user)) {onlineUsers.add(user);}userAdapter.notifyDataSetChanged();});}private void disconnect() {if (socket ! null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}Overrideprotected void onDestroy() {super.onDestroy();disconnect();} }代码解读 在我制作的时候,在写方法这里就遇到了问题,总的来说,就是匿名类内部调用方法,默认调用的是匿名类内部的方法,而不调用外部方法.但是我们一般定义类都在外部定义而不会在匿名类内部定义,所以就有找不到调用类的错误(下面有例子) 这里用MainActivity中的sendMessage方法来举例 上图片! 先不看报错部分,如果大致对比一下,就能发现代码好多行都不一样,是的,因为最上面展示的是更改和优化过的好代码 点开小红灯泡 就能看到 当当当当 创建方法? 我disconnect已经创建过了,为什么还要我创建 因为他没有找到啊 打个比方说,匿名类就像是封建派的老顽固,只用自己家有的,外来的?“我匿名类可是天朝上国,还需要你的方法?”(其实自家也没有) 而这个方法呢就像世界的先进技术,别人已经研究好的,拿来就能用,可悲的是他非要用自己的,那怎么办? 不开国门做生意,那就打到你开为止,不用?那就逼着你用 所以强制他一下就好啦 架炮! 在报错这行代码的this前面加上外部类的的类名点,咱这里就是MainActivity.,加上后效果如下 这里加上**MainActivity.**就是限制了this必须调用外部类MainActivity里的方法disconnect 找不到我就硬塞给你,你还不能不要 但是改好了,还和最上面的好代码不一样啊 是的,上面的是用了Lambda表达式的,拆开代码单独看就是… 这个(老) 和这个(新) 的区别 一开始我以为他俩是等效的,只是后者是用了Lambda简化过的,代码更简洁了而已,但是他的进步远不止于此 可以看到他并没有被强制增加MainActivity.,这是为什么呢? 这是因为Lambda表达式中的 this 自动指向外部类实例因此可以直接使用 this::disconnect 说白了就是人家本身就开放,追求外界,没必要轰他 同理,简洁代码如下 将这个 换成这个 Lambda好处多多,在这里就不一一赘述了 所以咱家也是好起来了,与时俱进,都改用先进技术了 话说回来,先说控件的定义和初始化吧 成员变量声明 private Socket socket; //用于建立连接 private DataInputStream in; //特殊流接收 private DataOutputStream out; //特殊流发送 private Handler handler new Handler(Looper.getMainLooper());//用于更新主线程 private ListString messages new ArrayList();//定义存储消息的ArrayList private ArrayAdapterString messageAdapter;//消息显示适配器 private ListString onlineUsers new ArrayList();//定义存储在线用户的ArrayList private ArrayAdapterString userAdapter;//用户显示适配器 private EditText inputField; //文本输入框 private Button sendButton; //发送按钮 private ListView messageListView, userListView;//群聊消息列表和在线用户列表 private String nickname XXX; //用户名 使用实际的昵称在线用户和消息显示其实是一样的,这里就只拿消息来举例: 1.当我们客户端收到消息就把消息存到存储消息的ArrayList–messages中 2.消息存储好后我们要调用把他显示出来,这时候需要用到适配器,来解决用什么方式来显示的问题(不用的话太难看) 3.将存储消息的ArrayList–messages放到适配器里,选择显示方式,创建该适配器对象,并将该适配器对象调用在群聊消息列表ListView–messageListView中 打个比方: 现在要吃一顿饭,先拿到饭,找到合适的餐具,才能慢慢享用 详细看下面主线程注释 主线程 protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); //初始化控件,在布局文件中找到控件messageListView findViewById(R.id.message_list_view);userListView findViewById(R.id.user_list_view);inputField findViewById(R.id.input_field);sendButton findViewById(R.id.send_button); //消息适配器,适配器显示方式android.R.layout.simple_list_item_1,调用显示数据集合messagesmessageAdapter new ArrayAdapter(this, android.R.layout.simple_list_item_1, messages);//将适配器添加到显示窗口 messageListView.setAdapter(messageAdapter); //同上userAdapter new ArrayAdapter(this, android.R.layout.simple_list_item_1, onlineUsers);userListView.setAdapter(userAdapter); //监听按钮,点击发送消息,调用方法sendButton.setOnClickListener(v - sendMessage());// 连接到服务器,调用方法connectToServer();}与网络请求有关的方法和代码,是不能堆在主线程(一般是onCreate)的,因为Android怕这些耗时操作堵塞主线程,影响用户体验,这里的收发消息和获取用户名都是需要网络的,也就是耗时操作,都需要新开线程来进行 连接上线方法: connectToServer方法 private void connectToServer() {//开线程,进行耗时操作new Thread(() - {try {// 创建Socket对象并连接到服务器socket new Socket(192.168.68.206, 9999); // 替换为实际服务器IP和端口//初始化,给数据流连接位置in new DataInputStream(socket.getInputStream());out new DataOutputStream(socket.getOutputStream());// 发送登录请求out.writeInt(1);out.writeUTF(nickname);// 开始接收消息,调用方法receiveMessages();} catch (IOException e) {e.printStackTrace();//断开连接后,将用户从在线列表中移除handler.post(this::disconnect);}}).start();}登录请求发送消息类型1,发送用户名端口与服务端一致 发送消息方法: sendMessage private void sendMessage() {//从输入框获取消息String message inputField.getText().toString().trim();//消息不为空,则执行if (!message.isEmpty()) {//开线程,进行耗时操作new Thread(() - {try {//发送消息out.writeInt(2);out.writeUTF(message);//发送后清空输入框inputField.setText();} catch (IOException e) {e.printStackTrace();//断开连接后,将用户从在线列表中移除,调用方法handler.post(this::disconnect);}}).start();} }发送消息类型2,发送输入框获取的消息 String message inputField.getText().toString().trim();inputField这是一个引用指向一个实现了getText()方法的对象通常是EditText或TextView等视图组件。它代表了用户可以输入文本的地方getText()这是EditText类中的一个方法用来获取当前输入框内的文本内容。这个方法返回的是一个Editable对象而不是直接返回字符串类型toString()由于getText()返回的是Editable对象为了将其转换为String类型需要调用toString()方法。这样做是为了方便后续对文本的操作比如比较、存储或者展示等trim()这个方法的作用是去除字符串两端的空白字符包括空格、制表符、换行符等。这对于确保输入数据的有效性非常有用因为它可以避免因为意外输入的额外空白而导致逻辑错误或者界面显示问题 接收消息方法: receiveMessages private void receiveMessages() {//耗时任务new!new!new!new Thread(() - {try {//无限循环保证在线状态下,可以实时接收消息while (true) {//接收消息类型int type in.readInt();//处理消息switch (type) {//类型为1,读取发送来的用户个数,循环读取case 1:int count in.readInt();for (int i 0; i count; i) {String user in.readUTF();//添加到在线用户列表集合,调用方法updateOnlineUsers(user);}break;//类型为2,读取消息case 2:String msg in.readUTF();//添加到消息列表集合,调用方法updateMessage(msg);break;}}} catch (IOException e) {e.printStackTrace();//断开连接后,将用户从在线列表中移除handler.post(this::disconnect);}}).start();}添加消息方法: updateMessage private void updateMessage(final String message) {handler.post(() - {//将输入参数存入消息集合messages.add(message);//通知适配器,有新消息存入,更新显示内容messageAdapter.notifyDataSetChanged();});}添加在线用户方法: updateOnlineUsers private void updateOnlineUsers(String user) {handler.post(() - {//判断在线用户集合里是否存在新输入参数,有则不会重复添加if (!onlineUsers.contains(user)) {//参数添加到在线用户集合onlineUsers.add(user);}//通知适配器,有新用户名存入,更新显示内容userAdapter.notifyDataSetChanged();});}断开连接,删除管道方法: disconnect private void disconnect() {//管道无连接,即断开状态,客户端关闭管道if (socket ! null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}// 可以在这里添加重新连接逻辑或提示用户断开连接}关闭程序方法: onDestroy Overrideprotected void onDestroy() {super.onDestroy();disconnect();}调用父类这里是Activity类的onDestroy()方法。这是非常重要的一步因为它确保了所有必要的清理工作由父类完成。每个Activity都继承自Activity基类而该基类的onDestroy()方法可能包含一些必要的资源释放逻辑。忽略这一步可能会导致内存泄漏或其他未预期的行为 super.onDestroy();对了,别忘了最重要的一步: 配置网络 在AndroidManifest.xml中添加如下代码 uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE /完成后如下 我滴任务完成辣! 如有问题,可评论留言,鄙人会试着解决 新手刚上路,错误之处欢迎指出,大家共勉! } //通知适配器,有新用户名存入,更新显示内容 userAdapter.notifyDataSetChanged(); }); } 断开连接,删除管道方法:disconnectjava private void disconnect() {//管道无连接,即断开状态,客户端关闭管道if (socket ! null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}// 可以在这里添加重新连接逻辑或提示用户断开连接}关闭程序方法: onDestroy Overrideprotected void onDestroy() {super.onDestroy();disconnect();}调用父类这里是Activity类的onDestroy()方法。这是非常重要的一步因为它确保了所有必要的清理工作由父类完成。每个Activity都继承自Activity基类而该基类的onDestroy()方法可能包含一些必要的资源释放逻辑。忽略这一步可能会导致内存泄漏或其他未预期的行为 super.onDestroy();对了,别忘了最重要的一步: 配置网络 在AndroidManifest.xml中添加如下代码 uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE /完成后如下 [外链图片转存中…(img-8lTKFuqP-1739372330386)] 我滴任务完成辣! 如有问题,可评论留言,鄙人会试着解决 新手刚上路,错误之处欢迎指出,大家共勉!
http://www.dnsts.com.cn/news/83609.html

相关文章:

  • 泰州网站建设制作工作室seo优化网站的手段
  • 网站的网页国家和住房城乡建设部网站首页
  • 做网站还是微信小程序网络营销策划创意案例点评
  • 营销型网站设计招聘淘宝客是如何做网站与淘宝对接的
  • 培训网站图片手机餐饮网站开发
  • 制作企业网站教程柚子网站建设
  • 视频模板网站wordpress主题验证码
  • 网站建设 佛山小企业如何建网站
  • 佳木斯 两学一做 网站小程序代理注册
  • 网站怎么做竞价苏州知名网站建设设计
  • 网站建设的培训百度双站和响应式网站的区别
  • 怎样把建好的网站上传到互联网淘宝代运营费用多少钱
  • 网站开发设计工程师深圳营销型网站设计
  • 站长统计幸福宝2022年排行榜医院网站首页设计
  • 深圳网站导航屏显的企业网站应该怎么做
  • php做网站难吗成都做网站公司
  • 上海行业门户网站建设技术稿定设计官网首页
  • 做网站的企业广州wordpress恢复数据库文件
  • 浙江电商网站建设销售辽宁建筑工程网
  • 哪个建站平台较好泰国网站后缀
  • 河南企业网站优化建筑工程公司黄页
  • 网站备案后有什么好处icp许可证
  • 遵义高端网站建设我的网站搜索不到了
  • 一元夺宝网站建设wordpress作者认证
  • 外贸汽车网站制作购物网站的设计
  • 营销型网站托管莱州网站设计
  • 域名注册哪个平台比较好东莞关键词优化排名
  • 国外网站设计 网址成都装修公司前十名
  • 电子商务网站建设内涵东台做网站哪家便宜
  • 济源做网站怎么收费免费app开发工具