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">
     <intent-filter>
     <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
     </intent-filter>
    </receiver>

 </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="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>
(0)

相关推荐

  • Android控制闪光灯的方法(打开与关闭)

    本文实例讲述了Android控制闪光灯的方法.分享给大家供大家参考,具体如下: 最近想做一个手电筒,在网上搜到一点资料 首先闪光灯可以用android.hardware.camera来控制 1. 在Manifest.xml文件中添加权限 复制代码 代码如下: <uses-permission android:name="android.permission.CAMERA" /> 2. 打开闪光灯 try{ m_Camera = Camera.open(); Camera.P

  • Android软键盘弹出时的界面控制方法

    本文实例讲述了Android软键盘弹出时的界面控制方法.分享给大家供大家参考,具体如下: 有时候androidactivity弹出软键盘后布局改变 下面有三种模式可以改变软键盘弹出以后的显示形式 模式一:压缩模式软键盘弹出以后,会压缩原先的大小 我们可以在AndroidManifet.xml中对Activity进行设置.如: android:windowSoftInputMode="stateUnchanged|adjustResize" 模式二:平移模式 软键盘弹出以后,不会压缩原先

  • android 动态控制状态栏显示和隐藏的方法实例

    方法一:(经试验无效,但网上广为流传,也许是我使用方法不当,有待进一步验证--) android想要应用运行时全屏有一种方法是在activity的onCreat方法中加入如下代码:getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,                 WindowManager.LayoutParams.FLAG_FULLSCREEN);并且需要在setContentView()之前,否则无效过.从这么多的

  • Android提高之蓝牙传感应用实例

    前面文章介绍了Android利用麦克风采集并显示模拟信号的实现方法,这种采集手段适用于无IO控制.单纯读取信号的情况.如果传感器本身需要包含控制电路(例如采集血氧信号需要红外和红外线交替发射),那么传感器本身就需要带一片主控IC,片内采集并输出数字信号了.Android手机如何在不改硬件电路的前提下与这类数字传感器交互呢?可选的通信方式就有USB和蓝牙,两种方式各有好处:USB方式可以给传感器供电,蓝牙方式要自备电源:USB接口标准不一,蓝牙普遍支持SPP协议.本文就选择蓝牙方式做介绍,介绍An

  • Android基础之使用Fragment控制切换多个页面

    今天讲解一下Fragment的控制,主要是切换View和页面替换等操作.还有就是如何获取Fragment的管理对象,以及与Activity的通信方式.1.管理Fragment要在activity中管理fragment,需要使用FragmentManager. 通过调用activity的getFragmentManager()取得它的实例. •可以通过FragmentManager做一些事情, 包括: 使用findFragmentById()(用于在activity layout中提供一个UI的f

  • Android编程调用红外线遥控功能示例

    本文实例讲述了Android编程调用红外线遥控功能.分享给大家供大家参考,具体如下: Android API Demos中有红外线遥控的小例子,在网上找了很久相关的资料,发现比较少,或许找的方法不对. Github上有一个与之相关的开源项目https://github.com/timnew/AndroidInfrared,还没来得及学习.希望有相关资料或学习项目的大神们多指导 . /** * Android红外线遥控官方Demo * * @description: * @author ldm *

  • Android蓝牙开发深入解析

    1. 使用蓝牙的响应权限 复制代码 代码如下: <uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 2. 配置本机蓝牙模块 在这里首先要了解对蓝牙操作一个核心类BluetoothAdapter 复制代码 代码如下: Bluetoot

  • Android蓝牙通信聊天实现发送和接受功能

    很不错的蓝牙通信demo实现发送和接受功能,就用了两个类就实现了,具体内容如下 说下思路把 主要有两个类 主界面类 和 蓝牙聊天服务类 . 首先创建线程 实际上就是创建BluetoothChatService() (蓝牙聊天服务类) 这个时候把handler 传过去 这样就可以操作UI 界面了,在线程中不断轮询读取蓝牙消息,当主界面点击发送按钮时 调用BluetoothChatService 的发送方法write 方法,这里的write 方法 使用了handler 发送消息,在主界面显示,另一个

  • Android适配安卓6.0蓝牙通讯实现过程

    事先说明: 安卓蓝牙需要定位权限申请,在安卓6.0需要用户手动确认权限后才能使用,各位可以自行查询资料实现,如果嫌麻烦,可以用第三方Bmob集成好的工具类进行实现,详细可以看http://blog.csdn.net/qq_30379689/article/details/52223244 蓝牙连接过程: 1.查询用户是否开启蓝牙. 2.搜索附近的可用的蓝牙. 3.进行蓝牙配对. 4.进行蓝牙连接. 5.获取输入流和输出流. 6.发送消息. 晒上我自己画的美图: 实验效果图: 实现需要的权限:由于

  • Android应用中实现手势控制图片缩放的完全攻略

    一.概述 现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~ 我相信看图的整个步骤,大家或者说用户应该不希望被打断把~~~"我擦,竟然不能放大,什么玩意,卸了~~" , "我擦,竟然不能移动,留有何用,卸了~~". 哈~所以对于图片的预览,一来,我们要让用户爽:二来,我们作为开发者,也得知道如何实现~~~ 想要做到图片支持多点触控,自由的进行缩放.平移,需要了解几个

  • 控制Android LED灯颜色的代码实例

    很多Android手机上都配有LED灯,比如HTC的手机在充电.新来短信等时候都会有响应的指示,其实很简单的这都是NotificationManager的一些参数而已,下面Android123给大家说下如何通过代码控制LED灯的闪烁,因为有些机型没有LED灯或颜色种类较少,发布时需要真机观察. 复制代码 代码如下: final int ID_LED=19871103;         NotificationManager nm=(NotificationManager)getSystemSer

随机推荐