Android系统中的蓝牙连接程序编写实例教程
Bluetooth结构
1、JAVA层
frameworks/base/core/java/android/bluetooth/
包含了bluetooth的JAVA类。
2、JNI层
frameworks/base/core/jni/android_bluetooth_开头的文件
定义了bluez通过JNI到上层的接口。
frameworks/base/core/jni/android_server_bluetoothservice.cpp
调用硬件适配层的接口system/bluetooth/bluedroid/bluetooth.c
3、bluez库
external/bluez/
这是bluez用户空间的库,开源的bluetooth代码,包括很多协议,生成libbluetooth.so。
4、硬件适配层
system/bluetooth/bluedroid/bluetooth.c
包含了对硬件操作的接口
system/bluetooth/data/*
一些配置文件,复制到/etc/bluetooth/。
还有其他一些测试代码和工具。
简略介绍Bluetooth开发使用到的类
1、BluetoothAdapter,蓝牙适配器,可判断蓝牙设备是否可用等功能。
常用方法列举如下:
cancelDiscovery(),取消搜索过程,在进行蓝牙设备搜索时,如果调用该方法会停止搜索。(搜索过程会持续12秒)
disable()关闭蓝牙,也就是我们常说的禁用蓝牙。
enable()打开蓝牙,这个方法打开蓝牙但不会弹出提示,正常流程操作下,我们会让系统提示用户是否打开蓝牙设备。如下两行代码可轻松搞定。
Intentenabler=newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler,reCode);//同startActivity(enabler);(在主Activity启动一个二级Activity,reCode一般等于3,一定记得要在AndroidManifest.xml里面添加蓝牙权限)
getAddress()获取本地蓝牙地址
getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter
getName()获取本地蓝牙名称
getRemoteDevice(Stringaddress)根据蓝牙地址获取远程蓝牙设备
getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
isDiscovering()判断当前是否正在查找设备,是返回true
isEnabled()判断蓝牙是否打开,已打开返回true,否则,返回false
listenUsingRfcommWithServiceRecord(Stringname,UUIDuuid)根据名称,UUID创建并返回
BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步
startDiscovery()开始搜索,这是搜索的第一步
2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备
createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket
这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket
这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter
这个类有几个隐藏方法,涉及到蓝牙的自动配对,setPin,createBond,cancelPairingUserInput,等方法(需要通过java的反射,调用这几个隐藏方法)
3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不过了,既然是Socket,方法就应该都差不多,
这个类一种只有三个方法
两个重载的accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!
还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接
close()关闭!
4.BluetoothSocket,跟BluetoothServerSocket相对,是客户端
一共5个方法,不出意外,都会用到
close(),关闭
connect()连接
getInptuStream()获取输入流
getOutputStream()获取输出流
getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备
二、蓝牙设备的发现、查找。
1.基于安全性考虑,设置开启可被搜索后,Android系统会默认给出120秒的时间,其他设备能在这120秒内搜索到它。
Intentenable=newIntent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(enalbe,REQUEST_DISCOVERABLE);
2.搜索蓝牙设备
BluetoothAdapter_bluetooth=BluetoothAdapter.getDefaultAdapter();
_bluetooth.startDiscovery();
3.关闭蓝牙设备
BluetoothAdapter_bluetooth=BluetoothAdapter.getDefaultAdapter();
_bluetooth.disable();
4.创建蓝牙客户端
BluetoothSocketsocket=device.createRfcommSocketToServiceRecord(UUID.fromString(UUID号));
socket.connect();
4.创建蓝牙服务端
BluetoothServerSocket_serverSocket=_bluetooth.listenUsingRfcommWithServiceRecord(服务端名称,UUID.fromeString(UUID号));
BluetoothSocketsocket=_serverSocket.accept();
InputStreaminputStream=socket.getInputStream();
TCP/IP通信相关代码实现
注:tcp_ip模块需要在系统setting模块中,完成蓝牙的配对,只有配对成功的,才能进行socket通信(具体如何配对和如何自动配对,将在bluetoot3或者4中进行讲解)
AndridManifest.xml
<manifestxmlns:android="http://schemas.android.com/apk/res/android" package="com.example.thecaseforbluetooth" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15"/> <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permissionandroid:name="android.permission.BLUETOOTH"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <activity android:name=".BluetoothActivity" android:label="@string/title_activity_bluetooth"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
main.xml
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnSearch" android:text="查找设备" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnExit" android:text="退出应用" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnDis" android:text="设置可被发现" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnserver" android:text="启动服务端" /> <ToggleButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tbtnSwitch" android:text="开/关蓝牙设备" /> <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/lvDevices" /> </LinearLayout>
BluetoothActivity.java
packagecom.example.thecaseforbluetooth; importjava.util.ArrayList; importjava.util.Set; importandroid.app.Activity; importandroid.bluetooth.BluetoothAdapter; importandroid.bluetooth.BluetoothDevice; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.content.IntentFilter; importandroid.os.Bundle; importandroid.view.Menu; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.AdapterView; importandroid.widget.AdapterView.OnItemClickListener; importandroid.widget.ArrayAdapter; importandroid.widget.Button; importandroid.widget.ListView; importandroid.widget.Toast; importandroid.widget.ToggleButton; publicclassBluetoothActivityextendsActivity{ publicButtonsearchBtn;//搜索蓝牙设备 publicButtonexitBtn;//退出应用 publicButtondiscoverBtn;//设置可被发现 publicToggleButtonopenBtn;//开关蓝牙设备 publicButtonserverbtn; publicListViewlistView;//蓝牙设备清单 publicArrayAdapter<String>adapter; publicArrayList<String>list=newArrayList<String>(); privateBluetoothAdapterbluetoothAdapter=BluetoothAdapter.getDefaultAdapter(); Set<BluetoothDevice>bondDevices; publicContextcontext; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); searchBtn=(Button)findViewById(R.id.btnSearch); exitBtn=(Button)findViewById(R.id.btnExit); discoverBtn=(Button)findViewById(R.id.btnDis); openBtn=(ToggleButton)findViewById(R.id.tbtnSwitch); serverbtn=(Button)findViewById(R.id.btnserver); listView=(ListView)findViewById(R.id.lvDevices); context=getApplicationContext(); adapter=newArrayAdapter<String>(this,android.R.layout.simple_list_item_1,list); listView.setAdapter(adapter); openBtn.setChecked(false); //注册广播接收信号 IntentFilterintent=newIntentFilter(); intent.addAction(BluetoothDevice.ACTION_FOUND);//用BroadcastReceiver来取得搜索结果 intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//每当扫描模式变化的时候,应用程序可以为通过ACTION_SCAN_MODE_CHANGED值来监听全局的消息通知。比如,当设备停止被搜寻以后,该消息可以被系统通知給应用程序。 intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//每当蓝牙模块被打开或者关闭,应用程序可以为通过ACTION_STATE_CHANGED值来监听全局的消息通知。 registerReceiver(searchReceiver,intent); //显示已配对设备以及搜索未配对设备 searchBtn.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub if(bluetoothAdapter.isDiscovering()){ bluetoothAdapter.cancelDiscovery(); } list.clear(); bondDevices=bluetoothAdapter.getBondedDevices(); for(BluetoothDevicedevice:bondDevices){ Stringstr="已配对完成"+device.getName()+"" +device.getAddress(); list.add(str); adapter.notifyDataSetChanged(); } bluetoothAdapter.startDiscovery(); } }); //退出应用 exitBtn.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub BluetoothActivity.this.finish(); } }); //设置蓝牙设备可发现 discoverBtn.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub IntentdiscoverIntent=newIntent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300); startActivity(discoverIntent); } }); //开关蓝牙设备 openBtn.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub if(openBtn.isChecked()==true){ bluetoothAdapter.disable(); } elseif(openBtn.isChecked()==false){ bluetoothAdapter.enable(); } } }); //作为服务端开启 serverbtn.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub ServerThreadserverThread=newServerThread(bluetoothAdapter,context); Toast.makeText(context,"server端启动",5000).show(); serverThread.start(); } }); listView.setOnItemClickListener(newItemClickListener()); } @Override publicvoidonStart(){ super.onStart(); //IfBTisnoton,requestthatitbeenabled. if(bluetoothAdapter==null){ Toast.makeText(context,"蓝牙设备不可用",5000).show(); } if(!bluetoothAdapter.isEnabled()){ IntentenableIntent=newIntent( BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent,3); } } @Override publicbooleanonCreateOptionsMenu(Menumenu){ getMenuInflater().inflate(R.menu.main,menu); returntrue; } privatefinalBroadcastReceiversearchReceiver=newBroadcastReceiver(){ @Override publicvoidonReceive(Contextcontext,Intentintent){ //TODOAuto-generatedmethodstub Stringaction=intent.getAction(); BluetoothDevicedevice=null; if(BluetoothDevice.ACTION_FOUND.equals(action)){ device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState()==BluetoothDevice.BOND_NONE){ Toast.makeText(context,device.getName()+"",5000).show(); Stringstr="未配对完成"+device.getName()+"" +device.getAddress(); if(list.indexOf(str)==-1)//防止重复添加 list.add(str); } adapter.notifyDataSetChanged(); } } }; publicclassItemClickListenerimplementsOnItemClickListener{ @Override publicvoidonItemClick(AdapterView<?>arg0,Viewarg1,intarg2, longarg3){ //TODOAuto-generatedmethodstub if(bluetoothAdapter.isDiscovering()) bluetoothAdapter.cancelDiscovery(); Stringstr=list.get(arg2); Stringaddress=str.substring(str.length()-17); BluetoothDevicebtDev=bluetoothAdapter.getRemoteDevice(address); ClientThreadclientThread=newClientThread(btDev,context); clientThread.start(); }} }
Bluetoothprotocol.java
packagecom.example.thecaseforbluetooth; publicinterfaceBluetoothprotocol{ publicstaticfinalStringPROTOCOL_SCHEME_L2CAP="btl2cap"; publicstaticfinalStringPROTOCOL_SCHEME_RFCOMM="btspp"; publicstaticfinalStringPROTOCOL_SCHEME_BT_OBEX="btgoep"; publicstaticfinalStringPROTOCOL_SCHEME_TCP_OBEX="tcpobex"; }
ServerThread.java
packagecom.example.thecaseforbluetooth; importjava.io.IOException; importjava.io.InputStream; importjava.io.OutputStream; importjava.util.UUID; importandroid.bluetooth.BluetoothAdapter; importandroid.bluetooth.BluetoothServerSocket; importandroid.bluetooth.BluetoothSocket; importandroid.content.Context; importandroid.os.Message; importandroid.util.Log; importandroid.widget.Toast; publicclassServerThreadextendsThread{ publicBluetoothServerSocketmserverSocket; publicBluetoothAdapterbluetoothAdapter; publicBluetoothSocketsocket; publicContextcontext; publicServerThread(BluetoothAdapterbluetoothAdapter,Contextcontext){ this.bluetoothAdapter=bluetoothAdapter; this.context=context; } publicvoidrun(){ try{ /*创建一个蓝牙服务器 *参数分别:服务器名称、UUID*/ mserverSocket=bluetoothAdapter.listenUsingRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM, UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); ///*接受客户端的连接请求*/ socket=mserverSocket.accept(); //下面代码作者偷懒,读写另外起一个线程最好 //接收数据 byte[]buffer=newbyte[1024]; intbytes; InputStreammmInStream=null; try{ mmInStream=socket.getInputStream(); }catch(IOExceptione1){ //TODOAuto-generatedcatchblock e1.printStackTrace(); } System.out.println("zhoulcserver"); while(true){ if((bytes=mmInStream.read(buffer))>0) { byte[]buf_data=newbyte[bytes]; for(inti=0;i<bytes;i++) { buf_data[i]=buffer[i]; } Strings=newString(buf_data); System.out.println(s+"zhoulcserverisin"); } } }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } }
ClientThread.java
packagecom.example.thecaseforbluetooth; importjava.io.IOException; importjava.io.OutputStream; importjava.util.UUID; importandroid.bluetooth.BluetoothDevice; importandroid.bluetooth.BluetoothSocket; importandroid.content.Context; importandroid.widget.Toast; publicclassClientThreadextendsThread{ publicBluetoothSocketsocket; publicBluetoothDevicedevice; publicContextcontext; publicClientThread(BluetoothDevicedevice,Contextcontext){ this.device=device; this.context=context; } publicvoidrun(){ try{ //创建一个Socket连接:只需要服务器在注册时的UUID号 socket=device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); //连接 socket.connect(); //下面代码作者偷懒,读写另外起一个线程最好 //发送数据 if(socket==null) { Toast.makeText(context,"链接失败",5000).show(); return; } System.out.println("zhoulcclient"); while(true){ try{ System.out.println("zhoulcclientisin"); Stringmsg="helloeverybodyIamclient"; OutputStreamos=socket.getOutputStream(); os.write(msg.getBytes()); }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } } catch(IOExceptione) { e.printStackTrace(); } } }
蓝牙设备之间自动配对
1、蓝牙设备之间自动配对,需要两个设备都安装进行配对的apk(网上好多自动配对的帖子都没有说明情况)
2、在自动匹配的时候想通过反射调用BluetoothDevice的setPin、createBond、cancelPairingUserInput实现设置密钥、配对请求创建、取消密钥信息输入等。
1)createBond()创建,最终会调到源码的BluetoothService的createBond(Stringaddress)方法,通过对源码浅显的了解,createBond主要是写入匹配密钥(BluetoothService的writeDockPin())以及进入jni注册回调函数onCreatePairedDeviceResult观察匹配结果
比如: //Pinsdidnotmatch,orremotedevicedidnotrespondtopin
//requestintime
//Werejectedpairing,ortheremotesiderejectedpairing.This
//happensifeithersidepresses'cancel'atthepairingdialog.
//Notsureifthishappens
//Otherdeviceisnotrespondingatall
//alreadybonded
等,在jni中创建了进行匹配的device("CreatePairedDevice"),这时bluetooth会发送一个ACTION_PAIRING_REQUEST的广播,只有当前会出现密钥框的蓝牙设备收到。写完密钥之后,发送广播给另外一个蓝牙设备接收,然后打开密钥输入框进行匹配。
2)setPin()设置密钥,通过查看setting源码,发现在确认输入密钥之后会调用setPin()(如果点取消,就会调用cancelPairingUserInput,取消密钥框),setPin具体通过D-BUS做了什么没有去深究,但是在调用setPin的时候会remove掉一个map里面的键值对(address:int),也就是我们在调用setPin之后如果再去调用onCreatePairedDeviceResult,则该方法一定返回false,并且出现下面的打印提示:cancelUserInputNative(B8:FF:FE:55:EF:D6)calledbutnonativedataavailable,ignoring.MaybethePasskeyAgentRequestwasalreadycancelledbytheremoteorbybluez.(因为该方法也会remove掉一个键值对)
3)cancelPairingUserInput()取消用户输入密钥框,个人觉得一般情况下不要和setPin(setPasskey、setPairingConfirmation、setRemoteOutOfBandData)一起用,这几个方法都会remove掉map里面的key:value(也就是互斥的)。
3、蓝牙耳机、手柄等一些无法手动配置的设备是如何完成自动配对的。
在源码里面有一个自动配对的方法,也就是把pin值自动设为“0000”
/*package*/synchronizedbooleanattemptAutoPair(Stringaddress){
if(!mBondState.hasAutoPairingFailed(address)&&
!mBondState.isAutoPairingBlacklisted(address)){
mBondState.attempt(address);
setPin(address,BluetoothDevice.convertPinToBytes("0000"));
returntrue;
}
returnfalse;
}
该方法是在底层回调到java层的onRequestPinCode方法时被调用,首先Checkifitsadock(正常输入的密钥,走正常配对方式,双方输入匹配值),然后再try0000onceifthedevicelooksdumb(涉及到Device.AUDIO_VIDEO相关部分如:耳机,免提等进入自动匹配模式)进行自动配对。
言归正传,虽然个人觉得自动配对需要双方乃至多方蓝牙设备都需要装上实现自动配对的apk,已经失去了自动配对的意义,但有可能还是会派上用场。下面我们看看现实情况的自动配对是什么样的吧。
由于BluetoothDevice配对的方法都是hide的,所以我们需要通过反射调用被隐藏的方法,现在基本都是通用的工具类型了,网上模式基本一样。
ClsUtils.java
packagecn.bluetooth; importjava.lang.reflect.Field; importjava.lang.reflect.Method; importandroid.bluetooth.BluetoothAdapter; importandroid.bluetooth.BluetoothDevice; importandroid.util.Log; publicclassClsUtils { publicstaticBluetoothDeviceremoteDevice=null; /** *与设备配对参考源码:platform/packages/apps/Settings.git */Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java */ @SuppressWarnings("unchecked") staticpublicbooleancreateBond(@SuppressWarnings("rawtypes")ClassbtClass,BluetoothDevicebtDevice) throwsException { MethodcreateBondMethod=btClass.getMethod("createBond"); BooleanreturnValue=(Boolean)createBondMethod.invoke(btDevice); returnreturnValue.booleanValue(); } /** *与设备解除配对参考源码:platform/packages/apps/Settings.git */Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java */ @SuppressWarnings("unchecked") staticpublicbooleanremoveBond(ClassbtClass,BluetoothDevicebtDevice) throwsException { MethodremoveBondMethod=btClass.getMethod("removeBond"); BooleanreturnValue=(Boolean)removeBondMethod.invoke(btDevice); returnreturnValue.booleanValue(); } @SuppressWarnings("unchecked") staticpublicbooleansetPin(ClassbtClass,BluetoothDevicebtDevice, Stringstr)throwsException { try { MethodremoveBondMethod=btClass.getDeclaredMethod("setPin", newClass[] {byte[].class}); BooleanreturnValue=(Boolean)removeBondMethod.invoke(btDevice, newObject[] {str.getBytes()}); Log.d("returnValue","setPinissuccess"+btDevice.getAddress()+returnValue.booleanValue()); } catch(SecurityExceptione) { //thrownewRuntimeException(e.getMessage()); e.printStackTrace(); } catch(IllegalArgumentExceptione) { //thrownewRuntimeException(e.getMessage()); e.printStackTrace(); } catch(Exceptione) { //TODOAuto-generatedcatchblock e.printStackTrace(); } returntrue; } //取消用户输入 @SuppressWarnings("unchecked") staticpublicbooleancancelPairingUserInput(ClassbtClass, BluetoothDevicedevice) throwsException { MethodcreateBondMethod=btClass.getMethod("cancelPairingUserInput"); //cancelBondProcess() BooleanreturnValue=(Boolean)createBondMethod.invoke(device); Log.d("returnValue","cancelPairingUserInputissuccess"+returnValue.booleanValue()); returnreturnValue.booleanValue(); } //取消配对 @SuppressWarnings("unchecked") staticpublicbooleancancelBondProcess(ClassbtClass, BluetoothDevicedevice) throwsException { MethodcreateBondMethod=btClass.getMethod("cancelBondProcess"); BooleanreturnValue=(Boolean)createBondMethod.invoke(device); returnreturnValue.booleanValue(); } /** * *@paramclsShow */ @SuppressWarnings("unchecked") staticpublicvoidprintAllInform(ClassclsShow) { try { //取得所有方法 Method[]hideMethod=clsShow.getMethods(); inti=0; for(;i<hideMethod.length;i++) { //Log.e("methodname",hideMethod.getName()+";andtheiis:" //+i); } //取得所有常量 Field[]allFields=clsShow.getFields(); for(i=0;i<allFields.length;i++) { //Log.e("Fieldname",allFields.getName()); } } catch(SecurityExceptione) { //thrownewRuntimeException(e.getMessage()); e.printStackTrace(); } catch(IllegalArgumentExceptione) { //thrownewRuntimeException(e.getMessage()); e.printStackTrace(); } catch(Exceptione) { //TODOAuto-generatedcatchblock e.printStackTrace(); } } }
Bluetooth1.java主activity,所有界面操作实现地方
packagecn.bluetooth; importjava.io.IOException; importjava.util.ArrayList; importjava.util.List; importandroid.app.Activity; importandroid.bluetooth.BluetoothAdapter; importandroid.bluetooth.BluetoothDevice; importandroid.bluetooth.BluetoothSocket; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.content.IntentFilter; importandroid.os.Bundle; importandroid.util.Log; importandroid.view.Menu; importandroid.view.View; importandroid.widget.AdapterView; importandroid.widget.ArrayAdapter; importandroid.widget.Button; importandroid.widget.ListView; importandroid.widget.Toast; importandroid.widget.ToggleButton; publicclassBluetooth1extendsActivity{ /**Calledwhentheactivityisfirstcreated.*/ ButtonbtnSearch,btnDis,btnExit; ToggleButtontbtnSwitch; ListViewlvBTDevices; ArrayAdapter<String>adtDevices; List<String>lstDevices=newArrayList<String>(); BluetoothAdapterbtAdapt; publicstaticBluetoothSocketbtSocket; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); //Button设置 btnSearch=(Button)this.findViewById(R.id.btnSearch); btnSearch.setOnClickListener(newClickEvent()); btnExit=(Button)this.findViewById(R.id.btnExit); btnExit.setOnClickListener(newClickEvent()); btnDis=(Button)this.findViewById(R.id.btnDis); btnDis.setOnClickListener(newClickEvent()); //ToogleButton设置 tbtnSwitch=(ToggleButton)this.findViewById(R.id.tbtnSwitch); tbtnSwitch.setOnClickListener(newClickEvent()); //ListView及其数据源适配器 lvBTDevices=(ListView)this.findViewById(R.id.lvDevices); adtDevices=newArrayAdapter<String>(this, android.R.layout.simple_list_item_1,lstDevices); lvBTDevices.setAdapter(adtDevices); lvBTDevices.setOnItemClickListener(newItemClickEvent()); btAdapt=BluetoothAdapter.getDefaultAdapter();//初始化本机蓝牙功能 //======================================================== //modifiedbywiley /* *if(btAdapt.getState()==BluetoothAdapter.STATE_OFF)//读取蓝牙状态并显示 *tbtnSwitch.setChecked(false);elseif(btAdapt.getState()== *BluetoothAdapter.STATE_ON)tbtnSwitch.setChecked(true); */ if(btAdapt.isEnabled()){ tbtnSwitch.setChecked(false); }else{ tbtnSwitch.setChecked(true); } //============================================================ //注册Receiver来获取蓝牙设备相关的结果 IntentFilterintent=newIntentFilter(); intent.addAction(BluetoothDevice.ACTION_FOUND);//用BroadcastReceiver来取得搜索结果 intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(searchDevices,intent); } privatefinalBroadcastReceiversearchDevices=newBroadcastReceiver(){ @Override publicvoidonReceive(Contextcontext,Intentintent){ Stringaction=intent.getAction(); Bundleb=intent.getExtras(); Object[]lstName=b.keySet().toArray(); //显示所有收到的消息及其细节 for(inti=0;i<lstName.length;i++){ StringkeyName=lstName.toString(); Log.e(keyName,String.valueOf(b.get(keyName))); } BluetoothDevicedevice=null; //搜索设备时,取得设备的MAC地址 if(BluetoothDevice.ACTION_FOUND.equals(action)){ device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState()==BluetoothDevice.BOND_NONE){ Stringstr="未配对|"+device.getName()+"|" +device.getAddress(); if(lstDevices.indexOf(str)==-1)//防止重复添加 lstDevices.add(str);//获取设备名称和mac地址 adtDevices.notifyDataSetChanged(); } }elseif(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){ device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); switch(device.getBondState()){ caseBluetoothDevice.BOND_BONDING: Log.d("BlueToothTestActivity","正在配对......"); break; caseBluetoothDevice.BOND_BONDED: Log.d("BlueToothTestActivity","完成配对"); //connect(device);//连接设备 break; caseBluetoothDevice.BOND_NONE: Log.d("BlueToothTestActivity","取消配对"); default: break; } } } }; @Override protectedvoidonDestroy(){ this.unregisterReceiver(searchDevices); super.onDestroy(); android.os.Process.killProcess(android.os.Process.myPid()); } classItemClickEventimplementsAdapterView.OnItemClickListener{ @Override publicvoidonItemClick(AdapterView<?>arg0,Viewarg1,intarg2, longarg3){ if(btAdapt.isDiscovering())btAdapt.cancelDiscovery(); Stringstr=lstDevices.get(arg2); String[]values=str.split("\\|"); Stringaddress=values[2]; Log.e("address",values[2]); BluetoothDevicebtDev=btAdapt.getRemoteDevice(address); try{ BooleanreturnValue=false; if(btDev.getBondState()==BluetoothDevice.BOND_NONE){ Toast.makeText(Bluetooth1.this,"远程设备发送蓝牙配对请求",5000).show(); //这里只需要createBond就行了 ClsUtils.createBond(btDev.getClass(),btDev); }elseif(btDev.getBondState()==BluetoothDevice.BOND_BONDED){ Toast.makeText(Bluetooth1.this,btDev.getBondState()+"....正在连接..",1000).show(); } }catch(Exceptione){ e.printStackTrace(); } } } classClickEventimplementsView.OnClickListener{ @Override publicvoidonClick(Viewv){ if(v==btnSearch)//搜索蓝牙设备,在BroadcastReceiver显示结果 { if(btAdapt.getState()==BluetoothAdapter.STATE_OFF){//如果蓝牙还没开启 Toast.makeText(Bluetooth1.this,"请先打开蓝牙",1000) .show(); return; } if(btAdapt.isDiscovering()) btAdapt.cancelDiscovery(); lstDevices.clear(); Object[]lstDevice=btAdapt.getBondedDevices().toArray(); for(inti=0;i<lstDevice.length;i++){ BluetoothDevicedevice=(BluetoothDevice)lstDevice[i]; Stringstr="已配对|"+device.getName()+"|" +device.getAddress(); lstDevices.add(str);//获取设备名称和mac地址 adtDevices.notifyDataSetChanged(); } setTitle("本机:"+btAdapt.getAddress()); btAdapt.startDiscovery(); }elseif(v==tbtnSwitch){//本机蓝牙启动/关闭 if(tbtnSwitch.isChecked()==false) btAdapt.enable(); elseif(tbtnSwitch.isChecked()==true) btAdapt.disable(); }elseif(v==btnDis)//本机可以被搜索 { IntentdiscoverableIntent=newIntent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300); startActivity(discoverableIntent); }elseif(v==btnExit){ try{ if(btSocket!=null) btSocket.close(); }catch(IOExceptione){ e.printStackTrace(); } Bluetooth1.this.finish(); } } } @Override publicbooleanonCreateOptionsMenu(Menumenu){ getMenuInflater().inflate(R.menu.main,menu); returntrue; } }
PairingRequest.java(重要部分,自动配对主要是这个部分完成,activity只是创建了一个配对请求)
packagecn.bluetooth; importandroid.bluetooth.BluetoothDevice; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.widget.Toast; publicclassPairingRequestextendsBroadcastReceiver{ StringstrPsw="0000"; finalStringACTION_PAIRING_REQUEST="android.bluetooth.device.action.PAIRING_REQUEST"; staticBluetoothDeviceremoteDevice=null; @Override publicvoidonReceive(Contextcontext,Intentintent){ if(intent.getAction().equals(ACTION_PAIRING_REQUEST)){ BluetoothDevicedevice=intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState()!=BluetoothDevice.BOND_BONDED){ try{ ClsUtils.setPin(device.getClass(),device,strPsw);//手机和蓝牙采集器配对 //ClsUtils.cancelPairingUserInput(device.getClass(), //device);//一般调用不成功,前言里面讲解过了 Toast.makeText(context,"配对信息"+device.getName(),5000) .show(); }catch(Exceptione){ //TODOAuto-generatedcatchblock Toast.makeText(context,"请求连接错误...",1000).show(); } } //*/ //pair(device.getAddress(),strPsw); } } }
AndroidManifest.xml启动activity,接收广播
<manifestxmlns:android="http://schemas.android.com/apk/res/android" package="cn.bluetooth" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15"/> <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permissionandroid:name="android.permission.BLUETOOTH"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <activity android:name=".Bluetooth1" android:label="@string/title_activity_bluetooth1"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <receiverandroid:name=".PairingRequest"> <intent-filter> <actionandroid:name="android.bluetooth.device.action.PAIRING_REQUEST"/> </intent-filter> </receiver> </application> </manifest>
main.xml布局
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnSearch" android:text="btnSearch" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnExit" android:text="btnExit" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnDis" android:text="btnDis" /> <ToggleButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tbtnSwitch" android:text="tbtnSwitch" /> <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/lvDevices" /> </LinearLayout>