目录
前言
本文主要讲述Android 11如何获取WIFI权限并通过TCP/IP协议使用Socket套接字与设备进行通信,由于通信背景是与嵌入式设备通信并不涉及到与互联网操作,所以在这之间没有什么协议加密等相关操作,由于笔者技术有限,本文中所提供源代码仅供参考。
接下来正文开始。
权限申请
Android使用Wifi通信得先有相应的权限,因为是与连接的设备直接的通信,所以要有这三个权限。权限在开发文件AndroidManifest.xml中申请。这几个权限就是获取连接wifi的主机地址,wifi连接的状态。没有这些权限就建立不了通信。
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
看好放置的位置,别放错了位置了,不然报错不管。当然,这几个权限不需要动态权限申请。
获取WIFI信息
之后咱就创建WIfiConnectManager这个工具类封装好变量。这一段就是单纯的使用WIfiManager获取到连接Wifi的地址,如果获取不到就返回null,基本上只有没有连接到wifi或者没有开启Wifi才会返回null。其中Handler就是消息机制,用于接收数据。
public class WifiConnectManager {
private final String ip;
private final int port;
private Socket socket;
public Activity context;
private Handler handler;
private WifiConnectListener connectListener;
public ExecutorService executorService = Executors.newCachedThreadPool();
private WifiConnectManager(Activity activity,String ip, int port){
this.context = activity;
this.ip = ip;
this.port = port;
}
public static WifiConnectManager getWifiConnect(Activity context, int port){
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if(wifiManager != null){
//TODO 获取WIIF 的ip
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
String ip = Formatter.formatIpAddress(dhcpInfo.gateway);
return new WifiConnectManager(context,ip,port);
}
return null;
}
}
建立Socket套接字连接
之后在这个类中增加方法connect,这个方法就用作建立连接。可以看到用到了WifiConnectListener,这个是内部类,在下面的全部代码中会放出来,这个类主要监听Wifi的连接状态。在这里用到了线程池(线程池的创建是用提供的方法,虽然Android提示要自己创建我觉得用他提供的就行)之后就是检测Socket是否连接上。当然,网络通信是后台操作 这个一定要注意。
public void connect(final WifiConnectListener listener){
this.connectListener = listener;
if(executorService.isShutdown()){
executorService = Executors.newCachedThreadPool();
}
executorService.execute(() -> {
if(socket != null && socket.isConnected()){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
socket = new Socket();
try {
socket.connect(new InetSocketAddress(ip,port),6000);
if(socket.isConnected()){
if(listener != null){
connectListener.onConnectSuccess();
return;
}
}
} catch (IOException e) {
e.printStackTrace();
}
if(connectListener != null) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
connectListener.onConnectError();
}
});
}
});
}
接收Wifi数据
还是在这个类中创建onGetData方法,这个方法得有个锁,切记只能执行一次,你也不想同时接收了多条数据导致出现奇奇怪怪的问题吧。
之后的这个bytes就很有讲究,他的大小取决于你的协议包一个包的字节的长度,如果有很多就需要你修改这个值了。这个就是引用InputStream的输入流通过while不断的获取数据,while的循环条件是socket保持通信状态,如果不再保持就赶紧跳出去。
public synchronized void onGetData(Handler handler){
this.handler = handler;
executorService.execute(() -> {
while (socket.isConnected()){
try {
InputStream stream = socket.getInputStream();
if(stream != null){
//注意这个10是只接收10个字节,如果你觉得你的数据很多。。这个要改
//是根据协议改
byte[] bytes = new byte[10];
int i = stream.read(bytes);
Message message = new Message();
message.what = 1;
message.obj = bytes;
WifiConnectManager.this.handler.sendMessage(message);
}else{
break;
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
if(connectListener != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
context.runOnUiThread(new Runnable() {
@Override
public void run() {
connectListener.onConnectCancel();
}
});
}
});
}
发送数据
发送数据就很简单,发送byte[]字节就可以,使用OutputStream发送,其他人提供的都是发送完就赶紧的close了,咱别这样,不然发一次就直接断开连接收不到数据了。保持好长连接状态(可能你还需要双线程去守护监听是否断开反正看你操作了)。
public synchronized void sendData(final byte[] bytes){
executorService.execute(() -> {
if(socket.isConnected()){
try {
OutputStream stream = socket.getOutputStream();
stream.write(bytes);
stream.flush();
//如果要保持长连接 就不要关闭
// stream.close();
return;
} catch (IOException e) {
e.printStackTrace();
}
}
context.runOnUiThread(new Runnable() {
@Override
public void run() {
if(connectListener != null){
connectListener.onConnectCancel();
}
}
});
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
综合代码
所有的方法整合起来就是这样的。
import android.app.Activity;
import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WifiConnectManager {
private final String ip;
private final int port;
private Socket socket;
public Activity context;
private Handler handler;
private WifiConnectListener connectListener;
public ExecutorService executorService = Executors.newCachedThreadPool();
private WifiConnectManager(Activity activity,String ip, int port){
this.context = activity;
this.ip = ip;
this.port = port;
}
public static WifiConnectManager getWifiConnect(Activity context, int port){
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if(wifiManager != null){
//TODO 获取WIIF 的ip
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
String ip = Formatter.formatIpAddress(dhcpInfo.gateway);
return new WifiConnectManager(context,ip,port);
}
return null;
}
public void connect(final WifiConnectListener listener){
this.connectListener = listener;
if(executorService.isShutdown()){
executorService = Executors.newCachedThreadPool();
}
executorService.execute(() -> {
if(socket != null && socket.isConnected()){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
socket = new Socket();
try {
socket.connect(new InetSocketAddress(ip,port),6000);
if(socket.isConnected()){
if(listener != null){
connectListener.onConnectSuccess();
return;
}
}
} catch (IOException e) {
e.printStackTrace();
}
if(connectListener != null) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
connectListener.onConnectError();
}
});
}
});
}
public synchronized void onGetData(Handler handler){
this.handler = handler;
executorService.execute(() -> {
while (socket.isConnected()){
try {
InputStream stream = socket.getInputStream();
if(stream != null){
//注意这个10是只接收10个字节,如果你觉得你的数据很多。。这个要改
//是根据协议改
byte[] bytes = new byte[10];
int i = stream.read(bytes);
Message message = new Message();
message.what = 1;
message.obj = bytes;
WifiConnectManager.this.handler.sendMessage(message);
}else{
break;
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
if(connectListener != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
context.runOnUiThread(new Runnable() {
@Override
public void run() {
connectListener.onConnectCancel();
}
});
}
});
}
public synchronized void sendData(final byte[] bytes){
executorService.execute(() -> {
if(socket.isConnected()){
try {
OutputStream stream = socket.getOutputStream();
stream.write(bytes);
stream.flush();
//如果要保持长连接 就不要关闭
// stream.close();
return;
} catch (IOException e) {
e.printStackTrace();
}
}
context.runOnUiThread(new Runnable() {
@Override
public void run() {
if(connectListener != null){
connectListener.onConnectCancel();
}
}
});
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
public void close(){
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public interface WifiConnectListener {
void onConnectSuccess();
void onConnectError();
void onConnectCancel();
}
}
使用方式
推荐在onCreate方法中创建初始化的方法inifSocket,之后再创建Handler消息机制接收。
private void initSocket() {
wifiConnectManager = WifiConnectManager.getWifiConnect(this,端口);
wifiConnectListener = new WifiConnectManager.WifiConnectListener() {
@Override
public void onConnectSuccess() {
Toast.makeText(MainActivity.mainActivity, "连接成功", Toast.LENGTH_SHORT).show();
wifiConnectManager.onGetData(handler);
}
@Override
public void onConnectError() {
Toast.makeText(MainActivity.mainActivity, "网络错误,请重试", Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectCancel() {
Toast.makeText(MainActivity.mainActivity, "连接断开", Toast.LENGTH_SHORT).show();
}
};
if(wifiConnectManager != null) {
wifiConnectManager.connect(wifiConnectListener);
}else{
Toast.makeText(this, "网络错误,请重试", Toast.LENGTH_SHORT).show();
}
}
消息机制接收:
public Handler handler = new Handler(Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
if(msg.what == 1){
//TODO 接收数据
byte[] bytes = (byte[])msg.obj;
StringBuilder stringBuilder = new StringBuilder();
for (byte b:bytes
) {
stringBuilder.append(String.format("%X",b)).append(" ");
}
//打印
Log.i("socket",stringBuilder.toString());
}
}
};
发送数据
if(connectManager != null){
//你要发送的字节
byte[] b1 = new byte[10];
wifiConnectManager.sendData(b1);
}