参考文章地址:http://www.cocoachina.com/android/20171016/20806.html
前端布局如下:
Activity中的全部代码,其中使用了butterknife实现View中控件的实例化。
package com.example.a260219.myapplication;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.net.DatagramPacket;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import utils.UDPSocket;
public class AndroidUdpNewAcitivity extends AppCompatActivity {
@InjectView(R.id.tv_receive_ip)
EditText tvReceiveIp;
@InjectView(R.id.tv_receive_port)
TextView tvReceivePort;
@InjectView(R.id.btn_start)
Button btnStart;
@InjectView(R.id.tv_service_msg)
EditText tvServiceMsg;
@InjectView(R.id.btn_send)
Button btnSend;
@InjectView(R.id.btn_reset)
Button btnReset;
@InjectView(R.id.tv_received_ip)
TextView tvReceivedIp;
@InjectView(R.id.tv_receive_msg)
TextView tvReceiveMsg;
private UDPSocket socket;
private String rip;//接收端ip
private String sip;//服务端ip
private int port;
private String message;
private DatagramPacket receivePacket;
int count = 0;
public Handler mhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
receivePacket = (DatagramPacket) msg.obj;
String strReceive = new String(receivePacket.getData(), 0, receivePacket.getLength());
tvReceivedIp.setText(receivePacket.getAddress().getHostAddress());
tvReceivePort.setText(receivePacket.getPort() + "");
tvReceiveMsg.setText(strReceive);
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_udp);
ButterKnife.inject(this);
}
public void myToast(String str) {
Toast.makeText(AndroidUdpNewAcitivity.this, str, Toast.LENGTH_SHORT).show();
}
@OnClick({R.id.btn_start, R.id.btn_send,R.id.btn_reset})
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_start:
if(TextUtils.isEmpty(tvReceiveIp.getText())||TextUtils.isEmpty(tvReceivePort.getText())){
myToast("接收端ip或者port不能为空");
return;
}
rip=tvReceiveIp.getText().toString();
port=Integer.parseInt(tvReceivePort.getText().toString());
if(socket==null){
socket = new UDPSocket(rip, port, this);
}
//注册回调函数
socket.setUIUpdateListener(new UDPSocket.UIUpdateListener() {
@Override
public void upUI(DatagramPacket receivePacket) {
Message msg = new Message();
msg.obj = receivePacket;
mhandler.sendMessage(msg);
}
});
break;
case R.id.btn_send:
if(TextUtils.isEmpty(tvServiceMsg.getText().toString())){
myToast("发送消息不能为空");
return;
}
message=tvServiceMsg.getText().toString();
++count;//记录点击发送的次数
socket.sendMessage(message+count);
break;
case R.id.btn_reset:
tvReceivedIp.setText("");
tvReceiveMsg.setText("");
break;
}
}
}
---------------------------
UDPSocket.java中的代码:
//接口规范
public interface UIUpdateListener {
void upUI(DatagramPacket receivePacket);
}
public void setUIUpdateListener(UIUpdateListener listener){
startUDPSocket(listener);
}
public void startUDPSocket(UIUpdateListener listener) {
if (client != null) return;
try {
client = new DatagramSocket(CLIENT_PORT);
if (receivePacket == null) {
receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
}
startSocketThread(listener);
} catch (SocketException e) {
e.printStackTrace();
}
}
/**
* 接收数据
*/
private void startSocketThread(final UIUpdateListener listener) {
clientThread = new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "clientThread is running...");
receiveMessage(listener);
}
});
isThreadRunning = true;
clientThread.start();
}
/**
* 处理接受到的消息
*/
private void receiveMessage(UIUpdateListener listener) {
while (isThreadRunning) {
try {
if (client != null) {
client.receive(receivePacket);
}
lastReceiveTime = System.currentTimeMillis();
Log.d(TAG, "receive packet success...");
} catch (IOException e) {
Log.e(TAG, "UDP数据包接收失败!线程停止");
stopUDPSocket();
e.printStackTrace();
return;
}
if (receivePacket == null || receivePacket.getLength() == 0) {
Log.e(TAG, "无法接收UDP数据或者接收到的UDP数据为空");
continue;
}
//解析接收到的 json 信息
// 每次接收完UDP数据后,重置长度。否则可能会导致下次收到数据包被截断。
if (receivePacket != null) {
listener.upUI(receivePacket);
receivePacket.setLength(BUFFER_LENGTH);
}
}
}
public void stopUDPSocket() {
isThreadRunning = false;
receivePacket = null;
if (clientThread != null) {
clientThread.interrupt();
}
if (client != null) {
client.close();
client = null;
}
if (timer != null) {
timer.exit();
}
/**
* 发送
*/
public void sendMessage(final String message) {
this.BROADCAST_MSG=message;
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
InetAddress targetAddress = InetAddress.getByName(BROADCAST_IP);
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), targetAddress, CLIENT_PORT);
client.send(packet);
// 数据发送事件
Log.d(TAG, "数据发送成功");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
-----------------
总结:
注意在发送之前,需要先点击开启。
在开启的方法中,红色加粗部分的代码
socket.setUIUpdateListener,
是注册的回调函数(后续解释这个回调函数),在这个函数中执行了接受消息的语句。接受和发送消息,需要使用
DatagramSocket套接字和DatagramPacket数据包组装发送和接收的数据,
因为发送和接受信息会阻塞线程,所以发送和接受的方法需要写在子线程中。
发送端:伪代码
1.首先,创建一个发送的DatagramSocket
client = new DatagramSocket(CLIENT_PORT);
2.获取接收端的ip地址
InetAddress local = null;
try {
// 换成服务器端IP
local = InetAddress.
getByName
(ip);
} catch (UnknownHostException e) {
e.printStackTrace();
}
3.定义发送信息的字节数组
byte[] messageByte = message.getBytes();
4.创建DatagramPacket包组装数据,然后发送出去
DatagramPacket p = new DatagramPacket(messageByte, msg_length, local,
server_port);
try {
s.send(p);
} catch (IOException e) {
e.printStackTrace();
}
接收端:
1.创建一个接收消息的字节数组,并规定数组的长度
byte[] message = new byte[1024];
2.创建接收的DatagramSocket
client = new DatagramSocket(CLIENT_PORT);
3.创建接收数据的DatagramPacket
receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
client.receive(receivePacket);
补充:说说回调函数。
参考地址:
https://blog.csdn.net/a78270528/article/details/46918601
在UDPSocket类中接收到数据以后,需要把接收的数据显示在界面上,使用的回调函数。
什么是回调函数,
简单来说:回调函数就是预留给系统调用的函数,而且我们往往知道该函数被调用的时机。就好比这个项目中,回调函数是预留给Activity调用的,当接收到信息的时候进行回调,接收到信息进行UI界面更新的过程就叫”回调“。
如下图:
1.在UDPSocket类中定义一个接口(规范)
2.在Activity中注册接口,提前告诉系统这里有UI的方法等待调用,该方法具体实现更新UI。
3.在UDPSocket类中定义方法开启接受信息的方法,收到消息后调用更新UI的接口,告诉系统要调用注册的监听里的方法了。