最近公司开发需要用到蓝牙,花了大约一天的时间总结整理了一下。主要是为了以后自己方便看。有需要的朋友可以看下。欢迎一起讨论。后面会带上demo。里面是实现了蓝牙搜索,配对,连接,数据互传。
首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。
<uses-permission android:name="android.permission.BLUETOOTH" />允许程序连接到已配对的蓝牙设备。 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />允许程序发现和配对蓝牙设备。
Android 6.0后需要加上 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
一、我们需要用到了几个相关的类:
1.BluetoothAdapter 顾名思义,蓝牙适配器,直到我们建立bluetoothSocket连接之前,都要不断操作它
BluetoothAdapter里的方法很多,常用的有以下几个:
cancelDiscovery() 根据字面意思,是取消发现,也就是说当我们正在搜索设备的时候调用这个方法将不再继续搜索
disable()关闭蓝牙
enable()打开蓝牙,这个方法打开蓝牙不会弹出提示,更多的时候我们需要问下用户是否打开,以下这两行代码同样是打开蓝牙,不过会提示用户:
Intemtenabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler,reCode);//同startActivity(enabler);
getAddress()获取本地蓝牙地址
getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter
getName()获取本地蓝牙名称
getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备
getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
isDiscovering()判断当前是否正在查找设备,是返回true
isEnabled()判断蓝牙是否打开,已打开返回true,否则,返回false
listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步
startDiscovery()开始搜索,这是搜索的第一步
2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备
createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket
这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket
这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter
3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不过了,既然是Socket,方法就应该都差不多,这个类一种只有三个方法
两个重载的accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!
还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接
close()这个就不用说了吧,关闭资源!
4.BluetoothSocket,跟BluetoothServerSocket相对,是客户端
一共5个方法,不出意外,都会用
close(),关闭
connect()连接
getInptuStream()获取输入流
getOutputStream()获取输出流
getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备
二、直接上代码。
MainActivity主要开启蓝牙,需要动态获取权限的获取权限。蓝牙传递数据分客户端和服务端,这里给一个入口,进客户端和服务端。
package com.qtwl.bluetooth;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements View.OnClickListener {
BluetoothAdapter mBluetoothAdapter;
public static final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button service = findViewById(R.id.service);
Button clent = findViewById(R.id.clent);
service.setOnClickListener(this);
clent.setOnClickListener(this);
if (Build.VERSION.SDK_INT >= 6.0) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1000);
}
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
if (!mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
// 设置蓝牙可见性,最多300秒
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(intent);
}
} else {
Toast.makeText(this, "没有蓝牙模块", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View view) {
if (mBluetoothAdapter == null) {
if (!mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
// 设置蓝牙可见性,最多300秒
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(intent);
return;
}
}
Intent intent = null;
// 要在两台设备上的应用之间创建连接,必须同时实现服务器端和客户端机制,因为其中一台设备必须开放服务器套接字,而另一台设备必须发起连接(使用服务器设备的 MAC 地址发起连接)。
// 服务器设备和客户端设备分别以不同的方法获得需要的 BluetoothSocket。服务器将在传入连接被接受时收到套接字。 客户端将在其打开到服务器的 RFCOMM 通道时收到该套接字。
switch (view.getId()) {
case R.id.service://服务端
intent = new Intent(this, ServiceActivity.class);
break;
case R.id.clent://客户端
intent = new Intent(this, ClentActivity.class);
break;
}
startActivity(intent);
}
}
ServiceActivity服务端的代码。
package com.qtwl.bluetooth;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* 连接为服务器
* 当您需要连接两台设备时,其中一台设备必须通过保持开放的 BluetoothServerSocket 来充当服务器。
* 1.通过调用 listenUsingRfcommWithServiceRecord(String, UUID) 获取 BluetoothServerSocket。
* 当客户端尝试连接此设备时,它会携带能够唯一标识其想要连接的服务的 UUID。 两个 UUID 必须匹配,在下一步中,连接才会被接受。
* 通过调用 accept() 开始侦听连接请求。
* 除非您想要接受更多连接,否则请调用 close()。
* 这将释放服务器套接字及其所有资源,但不会关闭 accept() 所返回的已连接的 BluetoothSocket。 与 TCP/IP 不同,RFCOMM 一次只允许每个通道有一个已连接的客户端,因此大多数情况下,在接受已连接的套接字后立即在 BluetoothServerSocket 上调用 close() 是行得通的。
*/
public class ServiceActivity extends BaseActivity {
BluetoothServerSocket mmServerSocket;
TextView title;
TextView tvReceive;
@SuppressLint("HandlerLeak")
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvReceive.setText(tvReceive.getText() + "\n" + msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
title = findViewById(R.id.tv_service_isStart);
tvReceive = findViewById(R.id.tv_service);
try {
mmServerSocket = BluetoothAdapter.getDefaultAdapter().listenUsingRfcommWithServiceRecord("com.qtwl.bluetooth", UUID.fromString(MainActivity.MY_UUID));
title.setText("蓝牙服务器开启成功");
} catch (IOException e) {
e.printStackTrace();
title.setText("蓝牙服务器开启失败");
}
//写一个线程来做服务器,等待客户端来连接它
new Thread() {
@Override
public void run() {
super.run();
while (true) {
try {
showToast("等待别人来连接");
BluetoothSocket socket = mmServerSocket.accept();
if (socket != null) {
Message msg = Message.obtain();
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024 * 128];
int count = is.read(buffer);
msg.obj = new String(buffer, 0, count, "utf-8");
handler.sendMessage(msg);
OutputStream os = socket.getOutputStream();
os.write("服务器收到了".getBytes("UTF-8"));
showToast("服务器做了返回");
}
} catch (IOException e) {
break;
}
}
}
}.start();
}
}
客户端的代码,主要是搜索附近的蓝牙,并配对。
package com.qtwl.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 作为客户端
*/
public class ClentActivity extends BaseActivity {
ListView mLvBle;//蓝牙列表
Button search;//搜索附近蓝牙
private List<BluetoothDevice> mDeviceList = new ArrayList<>();//蓝牙列表
private BleAdapter mAdapter;
BluetoothAdapter mBluetoothAdapter;
/**
* 广播接收者
*/
private BroadcastReceiver bTReceive = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i("BroadcastReceiver", "BluetoothDevice.ACTION_FOUND device = "+device.getName()+" "+device.getAddress());
for (BluetoothDevice d : mDeviceList) {
if (TextUtils.isEmpty(d.getAddress()) || TextUtils.isEmpty(device.getAddress()) || d.getAddress().equalsIgnoreCase(device.getAddress())) {
return;
}
}
mDeviceList.add(device);
mAdapter.notifyDataSetChanged();
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
Log.e("BroadcastReceiver", "BluetoothDevice.ACTION_BOND_STATE_CHANGED");
BluetoothDevice blnDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
setProgressMsg("正在配对..");
switch (blnDevice.getBondState()) {
case BluetoothDevice.BOND_BONDING://正在配对
setProgressMsg("正在配对..");
break;
case BluetoothDevice.BOND_BONDED://配对结束
mAdapter.notifyDataSetChanged();
setProgressMsg("配对完成,开始连接..");
startActivity(blnDevice);
break;
case BluetoothDevice.BOND_NONE://取消配对/未配对
showToast("配对失败!");
for (BluetoothDevice d : mDeviceList) {
if (d.getName().equalsIgnoreCase(blnDevice.getName())) {
mAdapter.notifyDataSetChanged();
return;
}
}
dismissProgressDialog();
default:
break;
}
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
Log.e("BroadcastReceiver", "BluetoothAdapter.ACTION_BOND_STATE_CHANGED");
Toast.makeText(context, "BluetoothAdapter.ACTION_BOND_STATE_CHANGED", Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clent);
mLvBle = findViewById(R.id.lv_ble);//蓝牙列表
search = findViewById(R.id.btn_search);//搜索附近蓝牙
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
registerBTReceiver();// 注册蓝牙相关的各种广播
//给蓝牙列表设置数据
mAdapter = new BleAdapter(this);
mAdapter.setData(mDeviceList);
mLvBle.setAdapter(mAdapter);
mLvBle.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (mBluetoothAdapter != null)
mBluetoothAdapter.cancelDiscovery();
BluetoothDevice device = mDeviceList.get(i);
bondBT(device);
}
});
search.setOnClickListener(new View.OnClickListener() {//点击搜索按钮
@Override
public void onClick(View view) {
if (mBluetoothAdapter.isDiscovering()) {
return;
}
mBluetoothAdapter.startDiscovery();
}
});
}
/**
* 注册广播
*/
public void registerBTReceiver() {
// 设置广播信息过滤
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND); //发现蓝牙事件
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//蓝牙扫描结束事件
// intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//状态改变
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
// 注册广播接收器,接收并处理搜索结果
registerReceiver(bTReceive, intentFilter);
}
@Override
protected void onStop() {
super.onStop();
mBluetoothAdapter.cancelDiscovery();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销广播
unregisterReceiver(bTReceive);
}
/**
* 绑定蓝牙
*/
private void bondBT(BluetoothDevice device) {
showProgressDialog("配对蓝牙开始");
// 搜索蓝牙设备的过程占用资源比较多,一旦找到需要连接的设备后需要及时关闭搜索
// 获取蓝牙设备的连接状态
int connectState = device.getBondState();
switch (connectState) {
case BluetoothDevice.BOND_NONE: // 未配对
setProgressMsg("开始配对");
try {
Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
createBondMethod.invoke(device);
} catch (Exception e) {
showToast("配对失败!");
dismissProgressDialog();
e.printStackTrace();
}
break;
case BluetoothDevice.BOND_BONDED: // 已配对
//进入连接界面
startActivity(device);
break;
}
}
//如果配对好了,就跳转到发消息界面
private void startActivity(BluetoothDevice device) {
dismissProgressDialog();
Intent intent = new Intent(this, ClentSendActivity.class);
intent.putExtra("device", device);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
}
ClentSendActivity客户端连接的代码。客户端连接服务器,并发送数据,然后收到服务器返回的数据。
package com.qtwl.bluetooth;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* 客户端发消息
*/
public class ClentSendActivity extends BaseActivity {
private BluetoothDevice device;
private BluetoothSocket bTSocket;
TextView btNmae;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clent_send);
btNmae = findViewById(R.id.bluetooth_name);
device = getIntent().getParcelableExtra("device");
btNmae.setText(device.getName() + " " + device.getAddress());
connectDevice();
}
/**
* 连接蓝牙
*/
private void connectDevice() {
showProgressDialog("正在连接蓝牙");
new Thread() {
@Override
public void run() {
super.run();
try {
bTSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(MainActivity.MY_UUID)); //创建一个Socket连接:只需要服务器在注册时的UUID号
bTSocket.connect(); //连接
showToast("连接成功");
dismissProgressDialog();
OutputStream os = bTSocket.getOutputStream();
os.write("test 测试数据".getBytes("UTF-8"));
showToast("写数据成功");
InputStream is = bTSocket.getInputStream();
byte[] buffer = new byte[1024 * 128];
int count = is.read(buffer);
String string = new String(buffer, 0, count, "utf-8");
showToast("读到数据 string = " + string);
} catch (IOException e) {
showToast("连接异常,请退出本界面再次尝试!");
dismissProgressDialog();
e.printStackTrace();
}
}
}.start();
}
}
主要功能上面都有,这个是上面的 源码