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/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>

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, 安卓
, 蓝牙
bluetooth
android蓝牙开发实例、android蓝牙通讯实例、android蓝牙实例、android 编写系统服务、android 蓝牙通信实例,以便于您获取更多的相关知识。

时间: 2024-09-01 17:14:43

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/

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

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

Android Studio中配置OpenCV库开发环境的教程_Android

1.下载进入官网(http://opencv.org/)下载OpenCV4Android并解压.目录结构如下图所示. 其中,sdk目录即是我们开发opencv所需要的类库:samples目录中存放着若干opencv应用示例(包括人脸检测等),可为我们进行android下的opencv开发提供参考:doc目录为opencv类库的使用说明及api文档等:而apk目录则存放着对应于各内核版本的OpenCV_2.4.3.2_Manager_2.4应用安装包.此应用用来管理手机设备中的opencv类库,在

Android编程中Tween动画和Frame动画实例分析_Android

本文实例讲述了Android编程中Tween动画和Frame动画实现方法.分享给大家供大家参考,具体如下: Animation主要有两种动画模式:Tween动画和Frame动画 Tween动画由四种类型组成 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 res目录下新建anim创建Tween.xml <?xml version="1.0" encoding="utf-8

Android App开发中自定义View和ViewGroup的实例教程_Android

ViewAndroid所有的控件都是View或者View的子类,它其实表示的就是屏幕上的一块矩形区域,用一个Rect来表示,left,top表示View相对于它的parent View的起点,width,height表示View自己的宽高,通过这4个字段就能确定View在屏幕上的位置,确定位置后就可以开始绘制View的内容了. View绘制过程View的绘制可以分为下面三个过程: MeasureView会先做一次测量,算出自己需要占用多大的面积.View的Measure过程给我们暴露了一个接口o

Android App中使用Glide加载图片的教程_Android

与其他图片加载库相同,Glide除了可以加载网络图片之外,也可以加载本地图片.甚至还可以从各种各样奇葩的数据源中加载图片. 加载网络图片很多情况下,我们使用图片加载库就是为了加载网络图片.网络操作是一个很复杂的东西.试想一下,如果没有图片加载库,我们就要手动去下载图片,缓存图片,最后再从文件里面读取bitmap并设置到Imageview里面.这还算好的,要是在Listview里面你会更头疼的.原因我就不说了,你懂的~~再加上各种各样的Bitmap操作,保准你再也不想撸代码了.而且Bitmap这东

Android应用中使用XmlSerializer序列化XML数据的教程_Android

首先,我们看一下什么是serializer,serializer就是串行化,又名序列化.它可并不只是简单的把对象保存在存储器上,它可以使我们在流中传输对象,使对象变的可以像基本数据一样传递. XmlSerializer是针对XML进行序列化的类库,我们先来看一下里面的常用方法: 基本方法1.创建一个xml文件的序列化器,返回的是一个Xml的 Serializer 对象. XmlSerializer = Xml.newSerializer(); 2.设置序列化器的输出路径和编码方式 FileOut

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

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

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

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