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()打开蓝牙,这个方法打开蓝牙但不会弹出提示,正常流程操作下,我们会让系统提示用户是否打开蓝牙设备。如下两行代码可轻松搞定。

Intent enabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enabler,reCode);//同startActivity(enabler);(在主Activity启动一个二级Activity,reCode一般等于3,一定记得要在AndroidManifest.xml里面添加蓝牙权限)
    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
    这个类有几个隐藏方法,涉及到蓝牙的自动配对,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秒内搜索到它。
    Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    startActivityForResult(enalbe,REQUEST_DISCOVERABLE);
2.搜索蓝牙设备
    BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();
    _bluetooth.startDiscovery();
3.关闭蓝牙设备
    BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();
    _bluetooth.disable();
4.创建蓝牙客户端
    BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(UUID号));
    socket.connect();
4.创建蓝牙服务端
    BluetoothServerSocket _serverSocket = _bluetooth.listenUsingRfcommWithServiceRecord(服务端名称,UUID.fromeString(UUID号));
    BluetoothSocket socket = _serverSocket.accept();
    InputStream inputStream = socket.getInputStream();

TCP/IP通信相关代码实现
注:tcp_ip模块需要在系统setting模块中,完成蓝牙的配对,只有配对成功的,才能进行socket通信(具体如何配对和如何自动配对,将在bluetoot3或者4中进行讲解)
AndridManifest.xml

<manifest xmlns: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-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android: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> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

main.xml

<LinearLayout xmlns: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

package com.example.thecaseforbluetooth; import java.util.ArrayList; import java.util.Set; import android.app.Activity; 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.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import android.widget.ToggleButton; public class BluetoothActivity extends Activity { public Button searchBtn;//搜索蓝牙设备 public Button exitBtn;//退出应用 public Button discoverBtn;//设置可被发现 public ToggleButton openBtn;//开关蓝牙设备 public Button serverbtn; public ListView listView;//蓝牙设备清单 public ArrayAdapter<String> adapter; public ArrayList<String> list =new ArrayList<String>(); private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); Set<BluetoothDevice> bondDevices ; public Context context ; @Override public void onCreate(Bundle savedInstanceState) { 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 = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,list); listView.setAdapter(adapter); openBtn.setChecked(false); //注册广播接收信号 IntentFilter intent = new IntentFilter(); 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(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if(bluetoothAdapter.isDiscovering()){ bluetoothAdapter.cancelDiscovery(); } list.clear(); bondDevices = bluetoothAdapter.getBondedDevices(); for(BluetoothDevice device : bondDevices) { String str = " 已配对完成 " + device.getName() +" " + device.getAddress(); list.add(str); adapter.notifyDataSetChanged(); } bluetoothAdapter.startDiscovery(); } }); //退出应用 exitBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub BluetoothActivity.this.finish(); } }); //设置蓝牙设备可发现 discoverBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Intent discoverIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverIntent); } }); //开关蓝牙设备 openBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if(openBtn.isChecked() == true){ bluetoothAdapter.disable(); } else if(openBtn.isChecked() == false){ bluetoothAdapter.enable(); } } }); //作为服务端开启 serverbtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub ServerThread serverThread = new ServerThread(bluetoothAdapter, context); Toast.makeText(context, "server 端启动", 5000).show(); serverThread.start(); } }); listView.setOnItemClickListener(new ItemClickListener()); } @Override public void onStart() { super.onStart(); // If BT is not on, request that it be enabled. if(bluetoothAdapter == null){ Toast.makeText(context, "蓝牙设备不可用", 5000).show(); } if (!bluetoothAdapter.isEnabled()) { Intent enableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, 3); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } private final BroadcastReceiver searchReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String action = intent.getAction(); BluetoothDevice device = 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(); String str = " 未配对完成 " + device.getName() +" " + device.getAddress(); if (list.indexOf(str) == -1)// 防止重复添加 list.add(str); } adapter.notifyDataSetChanged(); } } }; public class ItemClickListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { // TODO Auto-generated method stub if(bluetoothAdapter.isDiscovering()) bluetoothAdapter.cancelDiscovery(); String str = list.get(arg2); String address = str.substring(str.length() - 17); BluetoothDevice btDev = bluetoothAdapter.getRemoteDevice(address); ClientThread clientThread = new ClientThread(btDev, context); clientThread.start(); }} }

Bluetoothprotocol.java

package com.example.thecaseforbluetooth; public interface Bluetoothprotocol { public static final String PROTOCOL_SCHEME_L2CAP = "btl2cap"; public static final String PROTOCOL_SCHEME_RFCOMM = "btspp"; public static final String PROTOCOL_SCHEME_BT_OBEX = "btgoep"; public static final String PROTOCOL_SCHEME_TCP_OBEX = "tcpobex"; }

ServerThread.java

package com.example.thecaseforbluetooth; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.os.Message; import android.util.Log; import android.widget.Toast; public class ServerThread extends Thread { public BluetoothServerSocket mserverSocket; public BluetoothAdapter bluetoothAdapter; public BluetoothSocket socket; public Context context; public ServerThread(BluetoothAdapter bluetoothAdapter,Context context) { this.bluetoothAdapter = bluetoothAdapter; this.context = context; } public void run() { try { /* 创建一个蓝牙服务器 * 参数分别:服务器名称、UUID */ mserverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM, UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); // /* 接受客户端的连接请求 */ socket = mserverSocket.accept(); //下面代码作者偷懒,读写另外起一个线程最好 //接收数据 byte[] buffer = new byte[1024]; int bytes; InputStream mmInStream = null; try { mmInStream = socket.getInputStream(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } System.out.println("zhoulc server"); while(true){ if( (bytes = mmInStream.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); System.out.println(s+"zhoulc server is in"); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

ClientThread.java

package com.example.thecaseforbluetooth; import java.io.IOException; import java.io.OutputStream; import java.util.UUID; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.widget.Toast; public class ClientThread extends Thread { public BluetoothSocket socket; public BluetoothDevice device; public Context context; public ClientThread(BluetoothDevice device,Context context){ this.device = device; this.context = context; } public void run() { 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("zhoulc client"); while(true){ try { System.out.println("zhoulc client is in"); String msg = "hello everybody I am client"; OutputStream os = socket.getOutputStream(); os.write(msg.getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } } }

蓝牙设备之间自动配对
    1、蓝牙设备之间自动配对,需要两个设备都安装进行配对的apk(网上好多自动配对的帖子都没有说明情况)
    2、在自动匹配的时候想通过反射调用BluetoothDevice的setPin、createBond、cancelPairingUserInput实现设置密钥、配对请求创建、取消密钥信息输入等。
        1)createBond()创建,最终会调到源码的BluetoothService的createBond(String address)方法,通过对源码浅显的了解,createBond主要是写入匹配密钥(BluetoothService的writeDockPin())以及进入jni注册回调函数onCreatePairedDeviceResult观察匹配结果
比如:    // Pins did not match, or remote device did not respond to pin
            // request in time
            // We rejected pairing, or the remote side rejected pairing. This
            // happens if either side presses 'cancel' at the pairing dialog.
            // Not sure if this happens
            // Other device is not responding at all
            // already bonded
等,在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) called but no native data available, ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote or by bluez.(因为该方法也会remove掉一个键值对)
        3)cancelPairingUserInput()取消用户输入密钥框,个人觉得一般情况下不要和setPin(setPasskey、setPairingConfirmation、setRemoteOutOfBandData)一起用,这几个方法都会remove掉map里面的key:value(也就是互斥的)。

3、蓝牙耳机、手柄等一些无法手动配置的设备是如何完成自动配对的。
    在源码里面有一个自动配对的方法,也就是把pin值自动设为“0000”

/*package*/ synchronized boolean attemptAutoPair(String address) {
        if (!mBondState.hasAutoPairingFailed(address) &&
                !mBondState.isAutoPairingBlacklisted(address)) {
            mBondState.attempt(address);
            setPin(address, BluetoothDevice.convertPinToBytes("0000"));
            return true;
        }
        return false;
    }
该方法是在底层回调到java层的onRequestPinCode方法时被调用,首先 Check if its a dock(正常输入的密钥,走正常配对方式,双方输入匹配值),然后再 try 0000 once if the device looks dumb(涉及到Device.AUDIO_VIDEO相关部分如:耳机,免提等进入自动匹配模式)进行自动配对。

言归正传,虽然个人觉得自动配对需要双方乃至多方蓝牙设备都需要装上实现自动配对的apk,已经失去了自动配对的意义,但有可能还是会派上用场。下面我们看看现实情况的自动配对是什么样的吧。

由于BluetoothDevice配对的方法都是hide的,所以我们需要通过反射调用被隐藏的方法,现在基本都是通用的工具类型了,网上模式基本一样。
ClsUtils.java

package cn.bluetooth; import java.lang.reflect.Field; import java.lang.reflect.Method; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.util.Log; public class ClsUtils { public static BluetoothDevice remoteDevice=null; /** * 与设备配对 参考源码:platform/packages/apps/Settings.git * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java */ @SuppressWarnings("unchecked") static public boolean createBond(@SuppressWarnings("rawtypes") Class btClass, BluetoothDevice btDevice) throws Exception { Method createBondMethod = btClass.getMethod("createBond"); Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice); return returnValue.booleanValue(); } /** * 与设备解除配对 参考源码:platform/packages/apps/Settings.git * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java */ @SuppressWarnings("unchecked") static public boolean removeBond(Class btClass, BluetoothDevice btDevice) throws Exception { Method removeBondMethod = btClass.getMethod("removeBond"); Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice); return returnValue.booleanValue(); } @SuppressWarnings("unchecked") static public boolean setPin(Class btClass, BluetoothDevice btDevice, String str) throws Exception { try { Method removeBondMethod = btClass.getDeclaredMethod("setPin", new Class[] {byte[].class}); Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, new Object[] {str.getBytes()}); Log.d("returnValue", "setPin is success " +btDevice.getAddress()+ returnValue.booleanValue()); } catch (SecurityException e) { // throw new RuntimeException(e.getMessage()); e.printStackTrace(); } catch (IllegalArgumentException e) { // throw new RuntimeException(e.getMessage()); e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return true; } // 取消用户输入 @SuppressWarnings("unchecked") static public boolean cancelPairingUserInput(Class btClass, BluetoothDevice device) throws Exception { Method createBondMethod = btClass.getMethod("cancelPairingUserInput"); // cancelBondProcess() Boolean returnValue = (Boolean) createBondMethod.invoke(device); Log.d("returnValue", "cancelPairingUserInput is success " + returnValue.booleanValue()); return returnValue.booleanValue(); } // 取消配对 @SuppressWarnings("unchecked") static public boolean cancelBondProcess(Class btClass, BluetoothDevice device) throws Exception { Method createBondMethod = btClass.getMethod("cancelBondProcess"); Boolean returnValue = (Boolean) createBondMethod.invoke(device); return returnValue.booleanValue(); } /** * * @param clsShow */ @SuppressWarnings("unchecked") static public void printAllInform(Class clsShow) { try { // 取得所有方法 Method[] hideMethod = clsShow.getMethods(); int i = 0; for (; i < hideMethod.length; i++) { //Log.e("method name", hideMethod.getName() + ";and the i is:" // + i); } // 取得所有常量 Field[] allFields = clsShow.getFields(); for (i = 0; i < allFields.length; i++) { //Log.e("Field name", allFields.getName()); } } catch (SecurityException e) { // throw new RuntimeException(e.getMessage()); e.printStackTrace(); } catch (IllegalArgumentException e) { // throw new RuntimeException(e.getMessage()); e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

Bluetooth1.java 主activity,所有界面操作实现地方

package cn.bluetooth; import java.io.IOException; import java.util.ArrayList; import java.util.List; 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.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import android.widget.ToggleButton; public class Bluetooth1 extends Activity { /** Called when the activity is first created. */ Button btnSearch, btnDis, btnExit; ToggleButton tbtnSwitch; ListView lvBTDevices; ArrayAdapter<String> adtDevices; List<String> lstDevices = new ArrayList<String>(); BluetoothAdapter btAdapt; public static BluetoothSocket btSocket; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Button 设置 btnSearch = (Button) this.findViewById(R.id.btnSearch); btnSearch.setOnClickListener(new ClickEvent()); btnExit = (Button) this.findViewById(R.id.btnExit); btnExit.setOnClickListener(new ClickEvent()); btnDis = (Button) this.findViewById(R.id.btnDis); btnDis.setOnClickListener(new ClickEvent()); // ToogleButton设置 tbtnSwitch = (ToggleButton) this.findViewById(R.id.tbtnSwitch); tbtnSwitch.setOnClickListener(new ClickEvent()); // ListView及其数据源 适配器 lvBTDevices = (ListView) this.findViewById(R.id.lvDevices); adtDevices = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, lstDevices); lvBTDevices.setAdapter(adtDevices); lvBTDevices.setOnItemClickListener(new ItemClickEvent()); btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能 // ======================================================== // modified by wiley /* * if (btAdapt.getState() == BluetoothAdapter.STATE_OFF)// 读取蓝牙状态并显示 * tbtnSwitch.setChecked(false); else if (btAdapt.getState() == * BluetoothAdapter.STATE_ON) tbtnSwitch.setChecked(true); */ if (btAdapt.isEnabled()) { tbtnSwitch.setChecked(false); } else { tbtnSwitch.setChecked(true); } // ============================================================ // 注册Receiver来获取蓝牙设备相关的结果 IntentFilter intent = new IntentFilter(); 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); } private final BroadcastReceiver searchDevices = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Bundle b = intent.getExtras(); Object[] lstName = b.keySet().toArray(); // 显示所有收到的消息及其细节 for (int i = 0; i < lstName.length; i++) { String keyName = lstName.toString(); Log.e(keyName, String.valueOf(b.get(keyName))); } BluetoothDevice device = null; // 搜索设备时,取得设备的MAC地址 if (BluetoothDevice.ACTION_FOUND.equals(action)) { device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() == BluetoothDevice.BOND_NONE) { String str = " 未配对|" + device.getName() + "|" + device.getAddress(); if (lstDevices.indexOf(str) == -1)// 防止重复添加 lstDevices.add(str); // 获取设备名称和mac地址 adtDevices.notifyDataSetChanged(); } }else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){ device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDING: Log.d("BlueToothTestActivity", "正在配对......"); break; case BluetoothDevice.BOND_BONDED: Log.d("BlueToothTestActivity", "完成配对"); //connect(device);//连接设备 break; case BluetoothDevice.BOND_NONE: Log.d("BlueToothTestActivity", "取消配对"); default: break; } } } }; @Override protected void onDestroy() { this.unregisterReceiver(searchDevices); super.onDestroy(); android.os.Process.killProcess(android.os.Process.myPid()); } class ItemClickEvent implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { if(btAdapt.isDiscovering())btAdapt.cancelDiscovery(); String str = lstDevices.get(arg2); String[] values = str.split("\\|"); String address = values[2]; Log.e("address", values[2]); BluetoothDevice btDev = btAdapt.getRemoteDevice(address); try { Boolean returnValue = false; if (btDev.getBondState() == BluetoothDevice.BOND_NONE) { Toast.makeText(Bluetooth1.this, "远程设备发送蓝牙配对请求", 5000).show(); //这里只需要createBond就行了 ClsUtils.createBond(btDev.getClass(), btDev); }else if(btDev.getBondState() == BluetoothDevice.BOND_BONDED){ Toast.makeText(Bluetooth1.this, btDev.getBondState()+" ....正在连接..", 1000).show(); } } catch (Exception e) { e.printStackTrace(); } } } class ClickEvent implements View.OnClickListener { @Override public void onClick(View v) { 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 (int i = 0; i < lstDevice.length; i++) { BluetoothDevice device = (BluetoothDevice) lstDevice[i]; String str = " 已配对|" + device.getName() + "|" + device.getAddress(); lstDevices.add(str); // 获取设备名称和mac地址 adtDevices.notifyDataSetChanged(); } setTitle("本机:" + btAdapt.getAddress()); btAdapt.startDiscovery(); } else if (v == tbtnSwitch) {// 本机蓝牙启动/关闭 if (tbtnSwitch.isChecked() == false) btAdapt.enable(); else if (tbtnSwitch.isChecked() == true) btAdapt.disable(); } else if (v == btnDis)// 本机可以被搜索 { Intent discoverableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); } else if (v == btnExit) { try { if (btSocket != null) btSocket.close(); } catch (IOException e) { e.printStackTrace(); } Bluetooth1.this.finish(); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } }

PairingRequest.java (重要部分,自动配对主要是这个部分完成,activity只是创建了一个配对请求)

package cn.bluetooth; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class PairingRequest extends BroadcastReceiver { String strPsw = "0000"; final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; static BluetoothDevice remoteDevice = null; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_PAIRING_REQUEST)) { BluetoothDevice device = 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 (Exception e) { // TODO Auto-generated catch block Toast.makeText(context, "请求连接错误...", 1000).show(); } } // */ // pair(device.getAddress(),strPsw); } } }

AndroidManifest.xml 启动activity,接收广播

<manifest xmlns: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-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android: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> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".PairingRequest">

时间: 2024-10-26 15:30:35

Android系统中的蓝牙连接程序编写实例教程的相关文章

Android系统中的蓝牙连接程序编写实例教程_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/b

在Android系统中使用gzip进行数据传递实例代码_Android

接下来让我解说一下如何在Android系统中使用gzip进行数据传递 HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术.大流量的WEB站点常常使用GZIP压缩技术来减少文件大小减少文件大小有两个明显的好处一是可以减少存储空间二是通过网络传输文件时可以减少传输的时间.作者在写这篇博客时经过测试4.4MB的文本数据经过Gzip传输到客户端之后变为392KB压缩效率极高. 一.服务端 服务端有2种方式去压缩一种可以自己压缩但是更推荐第二种方式用PrintWriter作为输出流工具类代

Android系统中的进程管理:进程的创建

对于操作系统来说,进程管理是其最重要的职责之一. 考虑到这部分的内容较多,因此会拆分成几篇文章来讲解. 本文是进程管理系统文章的第一篇,会讲解Android系统中的进程创建. 本文适合Android平台的应用程序开发者,也适合对于Android系统内部实现感兴趣的读者. 概述 Android系统以Linux内核为基础,所以对于进程的管理自然离不开Linux本身提供的机制.例如: 通过fork来创建进行 通过信号量来管理进程 通过proc文件系统来查询和调整进程状态等 对于Android来说,进程

Winxp系统下使用蓝牙连接电脑听音乐的方法

  Winxp系统下使用蓝牙连接电脑听音乐的方法          步骤如下: 1.在任务栏蓝牙图标上单击右键,选择"添加蓝牙设备"; 2.在"Bluetooth 设置"界面点击下一步; 3.在搜索到的设备中选择需要连接的蓝牙耳机,点击"下一步"; 4.配对成功,点击下一步; 5.点击"不带显示屏的个人免提设备"点击下一步,然后点击"完成"; 6.在任务栏蓝牙图标上单击右键,点击"浏览我的 蓝牙位

在android系统中如何播放、暂停、停止歌曲?

问题描述 在android系统中如何播放.暂停.停止歌曲? 我在程序中设置了播放.暂停.停止三个按钮.当我点击播放按钮,这个按钮就消失了,显示的是暂停按钮.反之亦然.当我点击播放按钮时,程序不出错.再点击暂停按钮时,就会出错.贴出代码: package com.mpIlango; import java.io.IOException; import java.util.ArrayList; import android.app.Activity; import android.media.Med

opengl-OpenGL 在Android系统中的显示问题

问题描述 OpenGL 在Android系统中的显示问题 openGL在android系统中只能全屏显示吗?可以控制让它只在屏幕的某一个部分显示吗

Android 系统中 Location Service 的实现与架构

前言 定位服务是移动设备上最常用的功能之一,下文以 Android 源码为基础,详细分析了 Android 系统中定 位服务的架构和实现.定位服务是 Android 系统提供的一项系统服务,在 Android 系统中,所有系统服务 的架构都是类似的.只要明白其中一个,然后再去理解其他是很容易的.对于 Android 的应用开发人员来说 ,本文可以帮助他们了解他们所使用的 API 背后的实现.对于 Android 的系统开发人员来说,本文可以帮 助他们更好的了解 Android 系统架构.关于如何

我们在Windows 8系统中无线网络连接的设置方法

第一步.首先单击托盘中的网络图标,在弹出的右侧网络边栏中查看飞行模式是否开启,如果开启则需要关闭它.然后在Wi-Fi中找到私有的无线网络SSID(无线网络广播名称),单击"连接". 第二步.然后,在白色方框中输入该私有无线网络的安全密钥,如果感觉输入错误,可以单击右侧的眼睛图标查看输入的内容,确认无误后单击"下一步". 第三步.如果网络安全密钥输入正确,此时计算机已经能够通过无线网络连接到因特网了,即可开始使用.当然,无线网络连接还有最后一步,是否启用电脑之间的共享

如何在android系统中实现pkcs11,或者有什么可用的开源类库?

问题描述 如何在android系统中实现pkcs11,或者有什么可用的开源类库? 如何在android系统中实现pkcs11,或者有什么可用的开源类库? 解决方案 无法实现 Sun==Oracle => copying bad :)而且更严重的是,在android中没法用,以为没有能够支持的标准PKCS#11(尽管JB确实用一个专用的PKCS#11提供一些东西).并不确定是否代码是在OpenJDK,但是如果是的话,你可以试着移植.但是那基本是不可能的.因为你需要完整的PKCS#11 API,对于