绪论:
本人业余爱好者一个,无android基础,在开发android蓝牙的时候遇到了很多问题,其中最难受的还是基础不够好而看不懂别人在写什么。所以针对新手,我想写一个新手也完完全全能看懂的教程(连android studio都没用过的人除外)。
在完成文章之后我发现有点长,希望读者能耐心看完。如果有大神看到发现不好的地方欢迎指出,但请别喷,毕竟我只是个新手+业余的。
需要了解的蓝牙概念:
首先,我们需要搞清楚经典蓝牙和低功耗蓝牙两个的区别,这两者在android代码上的编写是不一样的。本文针对经典蓝牙。
其次,手机蓝牙<----->手机蓝牙,手机蓝牙<----->蓝牙模块 两种连接方式也是不同的,前者需要两个手机同时安装开发的APP,APP里面的代码指定了唯一的UUID用于通信,而后者只能在手机APP里面通过反射的方式获取外设蓝牙的UUID,然后连接通信。
开发流程:
第一步:设置蓝牙权限
<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!-- Android 5.0以上蓝牙还需要位置权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
以上代码放在AndroidManifest.xml的<application上面
第二步:打开蓝牙
首先定义一个全局变量:
private BluetoothAdapter mBluetoothAdapter;
以上放在onCreate上面。
其次打开蓝牙:
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
以上代码放在onCreate里面
第三步:搜索蓝牙设备
先等待蓝牙完全打开再开始搜索
while(!mBluetoothAdapter.isEnabled());
mBluetoothAdapter.startDiscovery();
以上代码放在打开蓝牙的代码之后
经典蓝牙的设备获取是通过广播获取的,所以我们要先定义一个广播:
private class BluetoothReceiver extends 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); bluetoothDeviceArrayList.add(device);
Device_Name.add(device.getName()+" "+device.getAddress());
}
//ShowDevice(); //此函数只是在ListView中显示搜索到的蓝牙设备,读者可以自己写到到想要显示的地方
}
}
以上代码放在OnCreate下面即可
广播代码中的blueDeviceArrayList与Device_Name是分别用来存放外部蓝牙设备对象与外部蓝牙设备名和地址的,所以需要定义全局变量
ArrayList<BluetoothDevice>bluetoothDeviceArrayList=new ArrayList<>();
ArrayList<String>Device_Name=new ArrayList<>();
以上代码放在OnCreate上面即可
定义了广播之后要创建广播的对象
BluetoothReceiver receiver;
以上代码放在OnCreate上面即可
创建广播变量后要绑定
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
receiver = new BluetoothReceiver();
registerReceiver(receiver, filter);
以上代码放在OnCreate里面即可
在APP关闭之后要解除绑定,这个函数应该是应用关闭之后自动调用,不需要管
@Override
protected void onDestroy()
{
unregisterReceiver(receiver);
super.onDestroy();
}
以上代码放在onCreate下面即可
第四步:连接蓝牙
先定义一个全局变量
BluetoothSocket btSocket;
以上代码放在onCreate上面
然后是连接蓝牙的代码
mBluetoothAdapter.cancelDiscovery();
BluetoothDevice device = bluetoothDeviceArrayList.get(position);
try {
Method clientMethod = device.getClass() .getMethod("createRfcommSocket", new Class[]{int.class});
btSocket = (BluetoothSocket) clientMethod.invoke(device, 1);
connect(btSocket);//连接设备
} catch (Exception e) {e.printStackTrace();}
以上代码我放于ListView的OnItemClick监听器中,所以第二行的device我是通过监听器函数中自带的参数去列表中获取,读者需要自行修改此行,关于connect函数是自己写的一个函数,我在接下来贴出代码
public void connect(final BluetoothSocket btSocket) {
try {
if (btSocket.isConnected()) { return; }
btSocket.connect();//连接
if (btSocket.isConnected()) {
new Readtask().start();
} else { btSocket.close(); }
} catch (Exception e) { e.printStackTrace(); }
}
以上代码放在onCreate下面,关于new Readtask().start();是执行读取的线程,在接下来会讲到
完成上述步骤蓝牙应该是连接成功了,为了能够清楚知道是否连接成功,读者可以自行在connect函数中加点上面显示以清楚现在蓝牙状态
第五步:发送数据
发送数据要开启一个发送数据的线程
public class WriteTask extends Thread{ //写数据线程
private String srt; public WriteTask(String str){
this.srt=str;
}
@Override
public void run(){
OutputStream outputStream=null;
byte[] st=srt.getBytes();
try{
outputStream=btSocket.getOutputStream();
outputStream.write(st);
}catch (Exception e){e.printStackTrace();}
}
}
以上代码放在onCreate下面
写好线程需要开始这个线程
new WriteTask("aaaa").start();
以上我是放在按钮的点击事件里面,读者可以自行选择,“aaaa”只是我正在测试时用的数据,读者可以自行修改
第六步:接收数据
接收数据需要一个接收线程
public class Readtask extends Thread{//读数据线程
@Override public void run(){
byte[] buffer = new byte[1024];
int bytes; InputStream inputStream ; //建立输入流读取数据
while (true) {
try {
inputStream = btSocket.getInputStream();
if ((bytes = inputStream.read(buffer)) > 0) {
byte[] buf_data= new byte[bytes];
for (int i = 0; i < bytes; i++) {
buf_data[i] = buffer[i];
}
String s = new String(buf_data);
Message msg = handler.obtainMessage();
msg.what = 1;
msg.obj = s;
handler.sendMessage(msg);
}
} catch (Exception e) { Log.e("TAG", e.toString());break;}
}
if (btSocket != null) {
try {
btSocket.close();
} catch (Exception e) {Log.e("TAG", e.toString());}
}
}
}
此代码放在onCreate下面,关于新接触的handler我接下来会讲
设定好线程之后我们需要启动线程,关于线程的启动,在connect函数中已经写好
new Readtask().start();
以上代码放在connect函数中,请往上翻看
关于handler,是一个对象
Handler handler=new Handler() {
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what == 1){
String s=msg.obj.toString();
// txv.setText(s);
}
}
};
以上代码放在onCreate下面,s为接收到的字符串,读者可以自行操作它
Handler类的作用大概是一种机制,使自定义线程与主线程之间的通信,具体读者如果感兴趣可以自行百度。一开始我是在进程中直接对读取的字符串进行操作,但后来出现不明问题,我想可能是读取线程一直在循环而时序可能出现冲突,所以一定要用Handler这种方法。
以上描述可以实现经典蓝牙模块的通信,但我在使用的时候出现手机端接收到蓝牙模块数据丢失一字节的问题,由于要趁热打铁,所以我先写了这篇文章,如果后续有朋友遇到相同的问题又解决了,欢迎告知解决方法,谢谢!
关于上述丢数据的问题,我觉得可能是我发一串数据过去的时候手机分成多个数据段去接收了,而我显示只是显示最新接收的数据,导致看起来像丢包,所以最保险的方法是一个一个字节发送。