返回顶部
首页 > 资讯 > 移动开发 >Android系统中的蓝牙连接程序编写实例教程
  • 321
分享到

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

连接程序教程Android蓝牙 2022-06-06 08:06:28 321人浏览 薄情痞子
摘要

Bluetooth结构 1、JAVA层 frameworks/base/core/java/Android/bluetooth/ 包含了bluetooth的JAVA类。 2、J

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

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;
  
  @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(); 
  } 
  
  @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(); 
  } 
  
  @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 {
 
  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.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蓝牙开发深入解析详解Android——蓝牙技术 带你实现终端间数据传输Android单片机与蓝牙模块通信实例代码Android Bluetooth蓝牙技术使用流程详解分享Android 蓝牙4.0(ble)开发的解决方案android实现蓝牙文件发送的实例代码,支持多种机型Android手机通过蓝牙连接佳博打印机的实例代码Android 获取蓝牙Mac地址的正确方法Android蓝牙通信聊天实现发送和接受功能Android实现蓝牙(BlueTooth)设备检测连接


--结束END--

本文标题: Android系统中的蓝牙连接程序编写实例教程

本文链接: https://lsjlt.com/news/25126.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作