前面介绍了Android使用UDP协议发送的代码,但要实现接收的功能却是比较复杂了,这里直接用前人写好的库事半功倍。
在GitHub上找到一个UDP的封装,详见文章末尾的源码。
APP搭建步骤
新建一个空白工程,在AndroidManifest.xml中添加网络使用权限,和上篇一样。
将UdpClient.java文件复制到和MainActivity.java同一个文件夹内。不要直接在文件夹内复制,在Android Studio上复制,会自动修改包名。
修改布局文件“activity_main.xml”,布局比较复杂,可看最后的效果图
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="连接状态:"/>
<TextView
android:id="@+id/tv_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:text="未连接"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="服务器IP地址:"/>
<EditText
android:id="@+id/et_ip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:text="192.168.31.34"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="服务器端口号:"/>
<EditText
android:id="@+id/et_port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:text="1234"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="连接"
android:onClick="onClick"
android:layout_weight="1"/>
<Button
android:id="@+id/btn_disconnect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="断开"
android:onClick="onClick"
android:layout_weight="1"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送内容:"/>
<EditText
android:id="@+id/et_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:text="lb8820265"/>
</LinearLayout>
<Button
android:id="@+id/btn_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="发送"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="接收内容:"/>
<Button
android:id="@+id/btn_clear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="清空接收"/>
</LinearLayout>
<ScrollView
android:id="@+id/sv_receive"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/et_receive"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/black"
android:background="@null"/>
</ScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
编写MainActivity.java函数,主要就是初始化监听,编写各个控件的相应函数。
package com.example.lb_socket_android;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private EditText et_ip;
private EditText et_port;
private EditText et_send;
private EditText et_receive;
private TextView tv_state;
private ScrollView sv_receive;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_ip= (EditText) findViewById (R.id.et_ip);
et_port= (EditText) findViewById (R.id.et_port);
et_send=(EditText) findViewById (R.id.et_send);
tv_state= (TextView) findViewById (R.id.tv_state);
et_receive= (EditText) findViewById (R.id.et_receive);
sv_receive= (ScrollView) findViewById (R.id.sv_receive);
UdpClient.getInstance().setOnDataReceiveListener(dataReceiveListener);
}
public void onClick(View view){
switch (view.getId()) {
case R.id.btn_clear:
et_receive.setText("");
break;
case R.id.btn_send:
if (UdpClient.getInstance().isConnect()) {
byte[] data=et_send.getText().toString().getBytes();
String str = new String(data);
Log.i("TAG_log",str);
UdpClient.getInstance().sendByteCmd(data,1001);
} else {
Toast.makeText(MainActivity.this,"尚未连接,请连接Socket",Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_connect:
String ip = et_ip.getText().toString();
String port = et_port.getText().toString();
if(TextUtils.isEmpty(ip)){
Toast.makeText(MainActivity.this,"IP地址为空",Toast.LENGTH_SHORT).show();
return;
}
if(TextUtils.isEmpty(port)){
Toast.makeText(MainActivity.this,"端口号为空",Toast.LENGTH_SHORT).show();
return;
}
UdpClient.getInstance().connect(ip, Integer.parseInt(port));
break;
case R.id.btn_disconnect:
UdpClient.getInstance().disconnect();
tv_state.setText("未连接");
break;
default:
break;
}
}
private UdpClient.OnDataReceiveListener dataReceiveListener = new UdpClient.OnDataReceiveListener() {
@Override
public void onConnectSuccess() {
Log.i("TAG_log","onDataReceive connect success");
tv_state.setText("已连接");
}
@Override
public void onConnectFail() {
Log.e("TAG_log","onDataReceive connect fail");
tv_state.setText("未连接");
}
@Override
public void onDataReceive(byte[] buffer, int size, int requestCode) {
//获取有效长度的数据
byte[] data = new byte[size];
System.arraycopy(buffer, 0, data, 0, size);
String oxValue = new String(data);
Log.i("TAG_log","onDataReceive requestCode = "+requestCode + ", content = "+oxValue);
et_receive.append(oxValue + "\n");
sv_receive.fullScroll(View.FOCUS_DOWN);
}
};
@Override
protected void onDestroy() {
UdpClient.getInstance().disconnect();
super.onDestroy();
}
}
运行方法
运行效果
问题
通信的问题解决了,那么接下来就是面向小车编程了,如何控制电机,如何控制速度等。
源码
GitHub:https://github.com/wanli-car/Examples/tree/master/Android/lb_socket_Android
Gitee:https://gitee.com/wanli-car/Examples/tree/master/Android/lb_socket_Android
"万里"树莓派小车汇总贴:
lb8820265的“万里”树莓派小车开源分享 - DIY/开源硬件专区 - 电子工程世界-论坛 (eeworld.com.cn)
目录:
2“万里”树莓派小车——python学习(Thonny的使用)
4“万里”树莓派小车——C++学习(编译与运行,geany使用)
5“万里”树莓派小车——wiringPi学习(延时与线程模拟定时器)
6“万里”树莓派小车——wiringPi学习(PWM与外部中断模拟定时器)
7“万里”树莓派小车——RPi.GPIO学习(PWM与外部中断模拟定时器)
10“万里”树莓派小车——socket学习(UDP两机通讯)
11“万里”树莓派小车——socket学习(Android发送)
12“万里”树莓派小车——socket学习(Android收发)
19“万里”树莓派小车——VSCode学习(多C文件链接调试)
21“万里”树莓派小车——电机控制学习(4轮速度控制)
22“万里”树莓派小车——手机遥控电机转动
666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666