android 蓝牙连接端(客户端)封装
分类:
IT文章
•
2025-01-15 21:45:43
0.权限 AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<activity
android:name=".DeviceListActivity"
android:configChanges="screenSize|keyboardHidden|orientation"
android:launchMode="singleInstance"
android:screenOrientation="portrait"/>
1.设备列表布局 activity_device_list.xml (主要就一个listview了)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center"
android:text="扫描到的蓝牙"/>
<ListView
android:id="@+id/listViewMessage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fastScrollEnabled="true"/>
</LinearLayout>
View Code
2.设备列表java代码 DeviceListActivity.java
package de.bvb.bluetoothchat;
import android.app.Activity;
import android.app.Dialog;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import de.bvb.bluetoothchat.utils.BlueToothConnectCallback;
import de.bvb.bluetoothchat.utils.BluetoothDeviceInfo;
import de.bvb.bluetoothchat.utils.ClientUtil;
/**
* Created by Administrator on 2017/06/01.
*/
public class DeviceListActivity extends Activity implements AdapterView.OnItemClickListener {
List<String> list;
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_device_list);
ListView listView = (ListView) findViewById(R.id.listViewMessage);
list = new ArrayList<>();
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
ClientUtil.getInstance().onCreate(this);
ClientUtil.getInstance().setOnFoundUnBondDeviceListener(new ClientUtil.OnFoundUnBondDeviceListener() {
@Override
public void foundUnBondDevice(BluetoothDevice unBondDevice) {
list.add(unBondDevice.getName() + "|" + unBondDevice.getAddress());
adapter.notifyDataSetChanged();
}
});
}
@Override
protected void onResume() {
super.onResume();
List<BluetoothDeviceInfo> bluetoothDeviceInfoList = ClientUtil.getInstance().scanDevice();
list.clear();
for (BluetoothDeviceInfo bluetoothDeviceInfo : bluetoothDeviceInfoList) {
list.add(bluetoothDeviceInfo.toString());
adapter.notifyDataSetChanged();
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Dialog dialog = new Dialog(this);
dialog.setTitle("正在连接..");
dialog.show();
String macAddress = list.get(position).split("\|")[1];//
ClientUtil.getInstance().connectRemoteDevice(macAddress, new BlueToothConnectCallback() {
@Override
public void connecting(String serverBlueToothAddress) {
}
@Override
public void connectSuccess(String serverBlueToothAddress) {
dialog.dismiss();
Toast.makeText(DeviceListActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
startActivity(new Intent(DeviceListActivity.this, ClientActivity.class));
}
@Override
public void connectFailure(IOException e) {
dialog.dismiss();
Toast.makeText(DeviceListActivity.this, "连接失败..", Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
ClientUtil.getInstance().unregisterReceiver(this);
}
}
View Code
// 注册收到消息以后的事件
ClientUtil.getInstance().setOnReceivedMessageListener(new ReceivedMessageListener() {
@Override
public void onReceiveMessage(final String messageContent) {
list.add(new MessageEntity(messageContent, true));
listViewAdapterMessage.setData(list);
}
@Override
public void onConnectionInterrupt(IOException e) {
btnSend.setEnabled(false);
etMessage.setEnabled(false);
Toast.makeText(ClientActivity.this, "连接中断", Toast.LENGTH_SHORT).show();
startActivity(new Intent(ClientActivity.this, DeviceListActivity.class));
}
});
// 发送消息
ClientUtil.getInstance().sendMessage(message);
list.add(new MessageEntity(message, false));
listViewAdapterMessage.setData(list);
package de.bvb.bluetoothchat.utils;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.text.TextUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* 客户端(连接端)工具类
*/
public class ClientUtil {
public static final String TAG = "BluetoothManagerUtil";
///////////////////////////////////////////////////////////////////////////
// 单例模式
private ClientUtil() { }
public static synchronized ClientUtil getInstance() {
return SingletonHolder.instance;
}
private static final class SingletonHolder {
private static ClientUtil instance = new ClientUtil();
}
///////////////////////////////////////////////////////////////////////////
private String serverBlueToothAddress; //连接蓝牙地址
private BluetoothSocket socket = null; // 客户端socket
private BluetoothAdapter bluetoothAdapter;
/** 打开蓝牙,注册扫描蓝牙的广播 onCreate()中执行.连接页面调用 */
public void onCreate(Activity activity) {
registerBluetoothScanReceiver(activity);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (null != bluetoothAdapter) { //本地蓝牙存在...
if (!bluetoothAdapter.isEnabled()) { //判断蓝牙是否被打开...
// 发送打开蓝牙的意图,系统会弹出一个提示对话框,打开蓝牙是需要传递intent的...
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//打开本机的蓝牙功能...使用startActivityForResult()方法...这里我们开启的这个Activity是需要它返回执行结果给主Activity的...
activity.startActivityForResult(enableIntent, Activity.RESULT_FIRST_USER);
Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
// 设置蓝牙的可见性,最大值3600秒,默认120秒,0表示永远可见(作为客户端,可见性可以不设置,服务端必须要设置)
displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
//这里只需要开启另一个activity,让其一直显示蓝牙...没必要把信息返回..因此调用startActivity()
activity.startActivity(displayIntent);
// 直接打开蓝牙
bluetoothAdapter.enable();//这步才是真正打开蓝牙的部分....
LogUtil.d(TAG, "打开蓝牙成功");
} else {
LogUtil.d(TAG, "蓝牙已经打开了...");
}
} else {
LogUtil.d(TAG, "当前设备没有蓝牙模块");
}
}
/** 扫描设备 onResume()中执行.连接页面调用 */
public List<BluetoothDeviceInfo> scanDevice() {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
LogUtil.e(TAG, "蓝牙状态异常");
return null;
}
List<BluetoothDeviceInfo> bluetoothDeviceInfoList = new ArrayList<>();
if (bluetoothAdapter.isDiscovering()) { // 如果正在处于扫描过程...
/** 停止扫描 */
bluetoothAdapter.cancelDiscovery(); // 取消扫描...
} else {
// 每次扫描前都先判断一下是否存在已经配对过的设备
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
BluetoothDeviceInfo bluetoothDeviceInfo;
for (BluetoothDevice device : pairedDevices) {
bluetoothDeviceInfo = new BluetoothDeviceInfo(device.getName() + "", device.getAddress() + "");
bluetoothDeviceInfoList.add(bluetoothDeviceInfo);
LogUtil.d(TAG, "已经匹配过的设备:" + bluetoothDeviceInfo.toString());
}
} else {
LogUtil.d(TAG, "没有已经配对过的设备");
}
/* 开始搜索 */
bluetoothAdapter.startDiscovery();
}
return bluetoothDeviceInfoList;
}
/** 通过Mac地址去尝试连接一个设备.连接页面调用 */
public void connectRemoteDevice(final String serverBlueToothAddress, BlueToothConnectCallback connectInterface) {
this.serverBlueToothAddress = serverBlueToothAddress;
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(serverBlueToothAddress);
ThreadPoolUtil.execute(new ConnectRunnable(device, connectInterface));
}
/** 广播反注册.连接页面调用 */
public void unregisterReceiver(Activity activity) {
if (receiver != null && receiver.getAbortBroadcast()) {
activity.unregisterReceiver(receiver);
}
}
/** 发送消息,在通信页面使用 */
public void sendMessage(String message) {
try {
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write(message + "
");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/** 收到消息的监听事件,在通信页面注册这个事件 */
public void setOnReceivedMessageListener(ReceivedMessageListener listener) {
if (listener != null) {
// 可以开启读数据线程
// MainHandler.getInstance().post(new ReadRunnable(listener));
ThreadPoolUtil.execute(new ReadRunnable(listener));
}
}
/** 关闭蓝牙,在app退出时调用 */
public void onExit() {
if (bluetoothAdapter != null) {
bluetoothAdapter.cancelDiscovery();
// 关闭蓝牙
bluetoothAdapter.disable();
}
closeCloseable(writer, socket);
}
/** 连接线程 */
class ConnectRunnable implements Runnable {
private BluetoothDevice device; // 蓝牙设备
private BlueToothConnectCallback connectInterface;
public ConnectRunnable(BluetoothDevice device, BlueToothConnectCallback connectInterface) {
this.device = device;
this.connectInterface = connectInterface;
}
@Override
public void run() {
if (null != device) {
try {
if (socket != null) {
closeCloseable(socket);
}
socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
// 连接
LogUtil.d(TAG, "正在连接 " + serverBlueToothAddress);
connectInterface.connecting(serverBlueToothAddress);
// Message.obtain(handler, MESSAGE_TYPE_SEND, "请稍候,正在连接服务器: " + serverBlueToothAddress).sendToTarget();
socket.connect();
MainHandler.getInstance().post(new Runnable() {
@Override
public void run() {
connectInterface.connectSuccess(serverBlueToothAddress);
LogUtil.d(TAG, "连接 " + serverBlueToothAddress + " 成功 ");
}
});
// 如果实现了连接,那么服务端和客户端就共享一个RFFCOMM信道...
// Message.obtain(handler, MESSAGE_TYPE_SEND, "已经连接上服务端!可以发送信息").sendToTarget();
// 如果连接成功了...这步就会执行...更新UI界面...否则走catch(IOException e)
// Message.obtain(handler, MESSAGE_ID_REFRESH_UI).sendToTarget();
// 屏蔽点击事件
// listViewMessage.setOnItemClickListener(null);
} catch (final IOException e) {
MainHandler.getInstance().post(new Runnable() {
@Override
public void run() {
connectInterface.connectFailure(e);
LogUtil.d(TAG, "连接" + serverBlueToothAddress + "失败 " + e.getMessage());
}
});
// e.printStackTrace();
}
}
}
}
private BufferedWriter writer = null;
class ReadRunnable implements Runnable {
private ReceivedMessageListener listener;
public ReadRunnable(ReceivedMessageListener listener) {
this.listener = listener;
}
public void run() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String content;
while (!TextUtils.isEmpty(content = reader.readLine())) {
final String finalContent = content;
MainHandler.getInstance().post(new Runnable() {
@Override
public void run() {
listener.onReceiveMessage(finalContent);
}
});
// Message.obtain(handler, MESSAGE_TYPE_RECEIVED, content).sendToTarget();
}
} catch (final IOException e) {
MainHandler.getInstance().post(new Runnable() {
@Override
public void run() {
LogUtil.d(TAG, "连接中断 " + e.getMessage());
listener.onConnectionInterrupt(e);
}
});
// 连接断开
// Message.obtain(handler, MESSAGE_ID_DISCONNECT).sendToTarget();
}
closeCloseable(reader);
}
}
private BroadcastReceiver registerBluetoothScanReceiver(Activity activity) {
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
activity.registerReceiver(receiver, filter);
return receiver;
}
public void setOnFoundUnBondDeviceListener(OnFoundUnBondDeviceListener onFoundUnBondDeviceListener) {
this.onFoundUnBondDeviceListener = onFoundUnBondDeviceListener;
}
private OnFoundUnBondDeviceListener onFoundUnBondDeviceListener;
public interface OnFoundUnBondDeviceListener {
void foundUnBondDevice(BluetoothDevice unBondDevice);
}
private void closeCloseable(Closeable... closeable) {
if (null != closeable && closeable.length > 0) {
for (int i = 0; i < closeable.length; i++) {
if (closeable[i] != null) {
try {
closeable[i].close();
} catch (IOException e) {
e.printStackTrace();
} finally {
closeable[i] = null;
}
}
}
}
}
/**
* 下面是注册receiver监听,注册广播...说一下为什么要注册广播...
* 因为蓝牙的通信,需要进行设备的搜索,搜索到设备后我们才能够实现连接..如果没有搜索,那还谈什么连接...
* 因此我们需要搜索,搜索的过程中系统会自动发出三个广播...这三个广播为:
* ACTION_DISCOVERY_START:开始搜索...
* ACTION_DISCOVERY_FINISH:搜索结束...
* ACTION_FOUND:正在搜索...一共三个过程...因为我们需要对这三个响应过程进行接收,然后实现一些功能,因此
* 我们需要对广播进行注册...知道广播的人应该都知道,想要对广播进行接收,必须进行注册,否则是接收不到的...
*/
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {//正在搜索过程...
// 通过EXTRA_DEVICE附加域来得到一个BluetoothDevice设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 如果这个设备是不曾配对过的,添加到list列表
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
if (null != onFoundUnBondDeviceListener) {
LogUtil.d(TAG, "发现没有配对过的设备:" + parseDevice2BluetoothDeviceInfo(device));
onFoundUnBondDeviceListener.foundUnBondDevice(device);
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//搜索结束后的过程...
LogUtil.d(TAG, "没有发现设备");
}
}
}
};
private String parseDevice2BluetoothDeviceInfo(BluetoothDevice device) {
if (device == null) {
return "device == null";
}
return new BluetoothDeviceInfo(device.getName(), device.getAddress()).toString();
}
}