关闭

[置顶] Android 蓝牙开发(一)蓝牙通信

标签: Android蓝牙Bluetooth通信
39306人阅读 评论(83) 收藏 举报
分类:

1 蓝牙基本操作

随着可穿戴设备的流行,研究蓝牙是必不可少的一门技术了。

总结了下蓝牙开发使用的一些东西分享一下。

蓝牙权限

首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。

<uses-permissionandroid:name="android.permission.BLUETOOTH" />

允许程序连接到已配对的蓝牙设备。

<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />

允许程序发现和配对蓝牙设备。


BluetoothAdapter

操作蓝牙主要用到的类 BluetoothAdapter类,使用时导包
import android.bluetooth.BluetoothAdapter;
源码具体位置frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

BluetoothAdapter 代表本地设备的蓝牙适配器。该BluetoothAdapter可以执行基本的蓝牙任务,例如启
动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并创建一个
BluetoothServerSocket监听来自其他设备的连接请求。

获取蓝牙适配器

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
开启蓝牙

 if(!mBluetoothAdapter.isEnabled()){
//弹出对话框提示用户是后打开
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
      //不做提示,直接打开,不建议用下面的方法,有的手机会有问题。
      // mBluetoothAdapter.enable();
}

获取本地蓝牙信息

//获取本机蓝牙名称
String name = mBluetoothAdapter.getName();
//获取本机蓝牙地址
String address = mBluetoothAdapter.getAddress();
Log.d(TAG,"bluetooth name ="+name+" address ="+address);
//获取已配对蓝牙设备
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
Log.d(TAG, "bonded device size ="+devices.size());
for(BluetoothDevice bonddevice:devices){
	Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress());
}

搜索设备

mBluetoothAdapter.startDiscovery();

停止搜索

mBluetoothAdapter.cancelDiscovery();

搜索蓝牙设备,该过程是异步的,通过下面注册广播接受者,可以监听是否搜到设备。

IntentFilter filter = new IntentFilter();
//发现设备
filter.addAction(BluetoothDevice.ACTION_FOUND);
//设备连接状态改变
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//蓝牙设备状态改变
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothReceiver, filter);

监听扫描结果

通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。其中参数intent可以获取蓝牙设备BluetoothDevice

该demo中是连接指定名称的蓝牙设备,BLUETOOTH_NAME为"Galaxy Nexus",如果扫描不到,记得改这个蓝牙名称。

private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			Log.d(TAG,"mBluetoothReceiver action ="+action);
			if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。
				//获取蓝牙设备
				BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				if(scanDevice == null || scanDevice.getName() == null) return;
				Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());
				//蓝牙设备名称
				String name = scanDevice.getName();
				if(name != null && name.equals(BLUETOOTH_NAME)){
					mBluetoothAdapter.cancelDiscovery();
					//取消扫描
					mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting));					//连接到设备。
					mBlthChatUtil.connect(scanDevice);
				}
			}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){

			}
		}
		
	};

设置蓝牙可见性

有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。

可见时间默认值为120s,最多可设置300。

if (mBluetoothAdapter.isEnabled()) {
	if (mBluetoothAdapter.getScanMode() != 
			BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
		Intent discoverableIntent = new Intent(
				BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		discoverableIntent.putExtra(
				BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
		startActivity(discoverableIntent);
	}
}

2 服务端

android 蓝牙之间可以通过SDP协议建立连接进行通信,通信方式类似于平常使用socket。

首先创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,如下图所示为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。

其中的uuid需要服务器端和客户端进行统一。

private class AcceptThread extends Thread {
        // 本地服务器套接字
        private final BluetoothServerSocket mServerSocket;
        public AcceptThread() {       	
            BluetoothServerSocket tmp = null;
            // 创建一个新的侦听服务器套接字
            try {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(
                		SERVICE_NAME, SERVICE_UUID);
            	//tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);
            } catch (IOException e) {
                Log.e(TAG, "listen() failed", e);
            }
            mServerSocket = tmp;
        }

        public void run() {
            BluetoothSocket socket = null;
            // 循环,直到连接成功
            while (mState != STATE_CONNECTED) {
                try {
                    // 这是一个阻塞调用 返回成功的连接
                    // mServerSocket.close()在另一个线程中调用,可以中止该阻塞
                    socket = mServerSocket.accept();
                } catch (IOException e) {
                    Log.e(TAG, "accept() failed", e);
                    break;
                }
                // 如果连接被接受
                if (socket != null) {
                    synchronized (BluetoothChatUtil.this) {
                        switch (mState) {
                        case STATE_LISTEN:
                        case STATE_CONNECTING:
                            // 正常情况。启动ConnectedThread。
                            connected(socket, socket.getRemoteDevice());
                            break;
                        case STATE_NONE:
                        case STATE_CONNECTED:
                            // 没有准备或已连接。新连接终止。
                            try {
                                socket.close();
                            } catch (IOException e) {
                                Log.e(TAG, "Could not close unwanted socket", e);
                            }
                            break;
                        }
                    }
                }
            }
            if (D) Log.i(TAG, "END mAcceptThread");
        }

        public void cancel() {
            if (D) Log.d(TAG, "cancel " + this);
            try {
                mServerSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of server failed", e);
            }
        }
    }
mServerSocket通过accept()等待客户端的连接(阻塞),直到连接成功或失败。


3 客户端

客户端主要用来创建RFCOMM socket,并连接服务端。

先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,

连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,

如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord

private class ConnectThread extends Thread {
        private BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
        public ConnectThread(BluetoothDevice device) {
            mmDevice = device;
            BluetoothSocket tmp = null;
            // 得到一个bluetoothsocket
            try {
            	mmSocket = device.createRfcommSocketToServiceRecord
            			(SERVICE_UUID);
            } catch (IOException e) {
                Log.e(TAG, "create() failed", e);
                mmSocket = null;
            }
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectThread");
            try { 
                // socket 连接,该调用会阻塞,直到连接成功或失败
                mmSocket.connect();
            } catch (IOException e) {
                connectionFailed();
                try {//关闭这个socket
                    mmSocket.close();
                } catch (IOException e2) {
                    e2.printStackTrace();
                }
                return;
            }
            // 启动连接线程
            connected(mmSocket, mmDevice);
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }
接着客户端socket主动连接服务端。连接过程中会自动进行配对,需要双方同意才可以连接成功。


4 数据传输

客户端与服务端连接成功后都会调用connected(mmSocket, mmDevice),创建一个ConnectedThread线程()。

该线程主要用来接收和发送数据。客户端和服务端处理方式一样。该线程通过socket获得输入输出流。

private  InputStream mmInStream = socket.getInputStream();

private  OutputStream mmOutStream =socket.getOutputStream();

发送数据

public void write(byte[] buffer) {
    try {
        mmOutStream.write(buffer);
        // 分享发送的信息到Activity
        mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)
                .sendToTarget();
    } catch (IOException e) {
        Log.e(TAG, "Exception during write", e);
    }
}
接收数据

线程循环进行接收数据。

public void run() {
    // 监听输入流
    while (true) {
        try {
            byte[] buffer = new byte[1024];
            // 读取输入流
            int bytes = mmInStream.read(buffer);
            // 发送获得的字节的ui activity
            Message msg = mHandler.obtainMessage(MESSAGE_READ);
            Bundle bundle = new Bundle();
            bundle.putByteArray(READ_MSG, buffer);
            msg.setData(bundle);
            mHandler.sendMessage(msg);          
        } catch (IOException e) {
            Log.e(TAG, "disconnected", e);
                connectionLost();
                break;
            }
        }
   }

demo下载:http://www.demodashi.com/demo/10676.html

欢迎扫一扫关注我的微信公众号,定期推送优质技术文章


21
2
查看评论

Android 蓝牙开发基本流程

此例子基于 android demo 对于一般的软件开发人员来说,蓝牙是很少用到的,尤其是Android的蓝牙开发,国内的例子很少     Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多...
  • q610098308
  • q610098308
  • 2015-04-24 16:42
  • 77395

Android Bluetooth蓝牙开发:Bluetooth蓝牙设备之间的连接建立(3)

 Android Bluetooth蓝牙开发:Bluetooth蓝牙设备之间的连接建立(3) Android Bluetooth蓝牙设备的连接编程模型和Java socket网络连接编程模型类型。Android不同设备间的蓝牙连接,首先在编程模型上分为“服务器端(server)”和“客户端(...
  • zhangphil
  • zhangphil
  • 2016-01-21 12:49
  • 7604

Android蓝牙开发全面总结

基本概念 安卓平台提供对蓝牙的通讯栈的支持,允许设别和其他的设备进行无线传输数据。应用程序层通过安卓API来调用蓝牙的相关功能,这些API使程序无线连接到蓝牙设备,并拥有P2P或者多端无线连接的特性。 蓝牙的功能: 1、扫描其他蓝牙设备 2、为可配对的蓝牙设备查询蓝牙适配器 ...
  • s13383754499
  • s13383754499
  • 2017-11-03 15:19
  • 563

【android 蓝牙开发——传统蓝牙】

相关博客:鸿洋微信推荐地址(Android蓝牙自动配对攻略)
  • da_caoyuan
  • da_caoyuan
  • 2017-03-11 22:21
  • 1887

Android蓝牙开发学习(传输数据到蓝牙模块)

版权声明:本文为博主原创文章,未经博主允许不得转载。蓝牙是一种支持设备短距离传输数据的无线技术,Android在2.0以后提供了这方面的支持。如今蓝牙的使用越来越普遍, 近期在项目中使用到了相关的蓝牙知识,因为ble的相关API需要Android4.3以上才支持,所以在项目中使用的还是传统蓝牙,在此...
  • ldslwh
  • ldslwh
  • 2017-06-16 00:00
  • 772

Android蓝牙开发详解

蓝牙传递数据是移动端经常使用的通信方式,Android中蓝牙模块依然很常用。本文就详细讲解Android蓝牙模块的使用方法。        1、使用蓝牙的响应权限 XML/HTML代码 uses-permission&#...
  • phenixyf
  • phenixyf
  • 2016-03-31 17:29
  • 1068

Android开发之蓝牙详解(一)

一.概述这篇文章是我学习Android开发官网以及网上一些其他文章总结而来,主要就是为了好好研究一下蓝牙开发,看完这篇文章以后,我们就知道了怎样使用蓝牙API完成建立蓝牙连接的必要四步:1.打开蓝牙;2.查找附近已配对或可用的设备;3.连接设备;4.设备间数据交换。由于文章比较长,为了方便大家的学习...
  • Small_Lee
  • Small_Lee
  • 2016-03-04 10:34
  • 11361

android蓝牙开发---与蓝牙模块进行通信

近半个月来一直在搞android蓝牙这方面,主要是项目需要与蓝牙模块进行通信。开头的进展很顺利,但因为蓝牙模块不在我这里,所以只能用手机测试。一开头就发现手机的蓝牙不能用,为了证明这点,我刷了四次不同不同系统的官方包,正式宣布手机的蓝牙报销了,于是和朋友换手机。在测试的过程中也是非常痛苦,放假了,同...
  • phenixyf
  • phenixyf
  • 2016-06-17 10:10
  • 1345

Android蓝牙开发教程(一)——搜索蓝牙设备

Android蓝牙功能的开发一直是很多新手头疼的问题,网上虽然也有很多教程贴,但大多都写得不是很好,不能让我们真正掌握蓝牙开发的基本知识。本教程主要讲解搜索蓝牙设备、蓝牙设备之间的连接和蓝牙之间的通讯三个主要模块。掌握了这三个,基本就能进行简单的蓝牙开发了。先上效果图界面比较简单。主要有三个按钮,两...
  • a1533588867
  • a1533588867
  • 2016-09-05 17:59
  • 4128

Android 蓝牙开发基本流程之“连接指定水木行蓝牙模块”

Android 蓝牙开发基本流程之“连接指定水木行蓝牙模块” 根据我们实际项目需求,总结了建立蓝牙连接“指定水木行蓝牙模块”的必要四步:1.添加权限并判断设备是否支持蓝牙;2.打开蓝牙;3.连接设备;4.设备间数据交换。 一、首先,要操作蓝牙,先要在AndroidManifest.x...
  • rlw_1009
  • rlw_1009
  • 2018-01-23 09:56
  • 55
    个人资料
    • 访问:733843次
    • 积分:7054
    • 等级:
    • 排名:第3874名
    • 原创:123篇
    • 转载:23篇
    • 译文:0篇
    • 评论:413条
    微信公众号
      分享Android相关文章、生活感悟等。
    QQ交流群
    群号 532259820
    博客专栏
    最新评论
    微信公众号
    微信关注我的公众号
    统计