蓝牙通信_模块蓝牙与手机蓝牙通信手机需要下载什么蓝牙工具 - CSDN
  • 蓝牙通信实现原理

    2019-06-05 13:46:28
    掌握Android 蓝牙通信实现原理 掌握Android 蓝牙通信
  • Android 开发进阶课程蓝牙通信  本教程介绍了Android 蓝牙通信的原理及具体实现,其中包括:客户端服务器结构和对等结构。然后介绍了基于Socket实现的蓝牙通信,以及基于蓝牙实现对等结构网络通讯。我们还...
  • 蓝牙是什么(Bluetooth): 一种短距离无线通信技术 爱立信公司创建 如今由蓝牙技术联盟(Bluetooth Special Interest Group,简称SIG)管理。 现在用的都是低功耗蓝牙 Android 4.3(API Level 18)开始引入...

    蓝牙是什么(Bluetooth):

    1. 一种短距离无线通信技术 
    2. 爱立信公司创建
    3. 如今由蓝牙技术联盟(Bluetooth Special Interest Group,简称SIG)管理。
    4. 现在用的都是低功耗蓝牙 Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)
    5. 在 5.0 以后才支持外设模式,

    无线通信方案:

    • NFC   一个近距离接触连接的通信技术  如把手机当公交卡用  消耗最低
    • 蓝牙  一种短距离无线通信技术
    • WIFI  。。。

    为什么要用蓝牙:

    • 低功率 便于电池供电设备工作
    • 使用方便,点对点连接  
    • 短距离,低成本,以及高速
    • 在智能设备的普及性高,应用广。

    理论没有详细了解 这里贴出网址有兴趣可以去看下 

    Android BLE的总结-概念篇

    Android蓝牙BLE的详细讲解   

     

    UUID:全局唯一标识 

    • UUID是根据一定算法,计算得到的一长串数字,这个数字的产生使用了多种元素,所以使得这串数字不会重复,每次生成都会产生不一样的序列,所以可以用来作为唯一标识。
    • 创建服务器端和客户端时都需要用UUID来创建 连接通讯时只有一样的才可以成功连接上
    • 可以代码生成 也可以用uuid生成器 
     UUID.randomUUID().toString().replace("-", "");

    uuid


    蓝牙通信的流程:

    1. 注册适配器打开蓝牙
    2. 注册广播监听蓝牙状态
    3. 搜索附近设备和已配对设备
    4. 选择未配对设备进行配对
    5. 选择已配对设备进行连接通信

    1、打开蓝牙:

    首先呢 先加权限 获取位置的权限属于高危权限 所以还需要动态调用:

    1、AndroidManifest.xml    <6.0定位权限那别人都说只加一个就好了 我的貌似不行 所以我就都加上了>

    <!-- 蓝牙通讯权限 -->
        <uses-permission android:name="android.permission.BLUETOOTH" />//一些配置连接蓝牙的权限 
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />//进行操作的权限
        <!-- 6.0以上需要的权限 -->
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    2、activity  

    检测位置权限有没有同意 没有的话无法进行附近搜索 所以直接退出应用

    @RequiresApi(api = Build.VERSION_CODES.M)
        @Override
        protected void onResume() {
            super.onResume();
            //动态获取权限
            checkBluetoothAndLocationPermission();
        }
    
        @RequiresApi(api = Build.VERSION_CODES.M)
        private void checkBluetoothAndLocationPermission() {
            //判断是否有访问位置的权限,没有权限,直接申请位置权限
            if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
                    || (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
                        Manifest.permission.ACCESS_FINE_LOCATION}, 2);
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
            switch (requestCode) {
                case 2:
                    //再次判断是否获取到权限 没有就关闭当前界面
                    for (int i : grantResults) {
                        if (i != PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(this, "Permission error !!!", Toast.LENGTH_SHORT).show();
                            finish();
                        }
                    }
                    break;
            }
        }

    在onCreate()方法里,获取蓝牙适配器

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    BluetoothAdapter这个类常用的方法有:

    • getDefaultAdapter: 获取本地的蓝牙适配器
    • enable(); 打开蓝牙(不带提示框 但是手机有自带的。。。)
    • disable(); 关闭蓝牙
    • isEnabled(): 本地蓝牙是否打开
    • getAddress(); 获取自己的蓝牙MAC地址
    • cancelDiscovery() 停止扫描
    • isDiscovering() 是否正在处于扫描过程中
    • getState():获取本地蓝牙适配器的状态
    • getScanMode(): 获取本地蓝牙适配器的当前蓝牙扫描模式。

    详细用法表

    打开蓝牙的两种方式:

    • 第一种打开方法: 调用enable 
    • 第二种打开方法 ,调用系统API去打开蓝牙
    mBluetoothAdapter.enable();
    //不会自动提示用户默认打开 有的手机还是会提示的
    
    
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
    startActivityForResult(intent, REQUEST_OPEN_BT_CODE); //CODE值只是标记可以更改
    //会以Dialog样式显示一个Activity , 我们可以在onActivityResult()方法去处理返回值

     


    接下来为了保险起见先判断设备是否支持蓝牙:

    if(mBluetoothAdapter == null){
                Toast.makeText(this,"本地蓝牙不可用",Toast.LENGTH_SHORT).show();
                finish();   //退出应用
            }

    确认支持蓝牙的话就可以调用蓝牙适配器的方法了:

    String Address = bluetoothAdapter.getAddress(); //获取本机蓝牙MAC地址  
    String Name = bluetoothAdapter.getName();   //获取本机蓝牙名称  
    // 若蓝牙没打开   
    if(!bluetoothAdapter.isEnabled()){  
      bluetoothAdapter.enable();  //打开蓝牙 
    }  

    上边这些除了 打开蓝牙其他的并没有什么用处 这里只是举个例子


    设置可以被搜索到  

    //设置可以被搜索到
    //判断蓝牙适配器的当前蓝牙扫描模式
                    if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)
                    {
                        Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                        // 设置被发现时间,最大值是3600秒,0表示设备总是可以被发现的(小于0或者大于3600则会被自动设置为120秒)
                        discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
                        startActivity(discoverableIntent);
                    }

    接下来开始搜索附近:

    判断是否正在搜索 如果是就停止搜索之类的 

    if (!mBluetoothAdapter.isDiscovering()) {//判断是否正在搜索
                        mBluetoothAdapter.startDiscovery();//开始搜索附近设备
                        textView2.setText("正在搜索...");
                      
                    } else {
                        mBluetoothAdapter.cancelDiscovery();//停止搜索
                    }

    搜索附近需要先注册个广播来监听查询附近蓝牙设备:

    蓝牙扫描时,扫描到任一远程蓝牙设备时,会发送此广播。两种广播用一个就行

    注册分为两种:静态注册和动态注册。

    • 静态注册就是在AndroidManifest.xml文件中定义,
    • 注册的广播接收器必须继承BroadReceiver 动态注册就是在程序中使用Context.registerReceiver注册。

    动态广播

    //注册广播 
            IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);//搜索到设备
            registerReceiver(receiver, filter);
            IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索结束
            registerReceiver(receiver, filter2);

    静态广播

    <!-- 广播接收 -->
            <receiver android:name="包名.类名" >
        		<intent-filter android:priority="1000">
            		<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>
            		<action android:name="android.bluetooth.device.action.FOUND" />
        		</intent-filter>
    	</receiver>
    

    new个BroadcastReceiver来接收:

    广播一旦发送 这边就会调用 onReceive()方法

    BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
    
                if (action.equals(BluetoothDevice.ACTION_FOUND)) {
                    //获取已配对蓝牙设备
                    Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
                     mPeiDuiList.clear();
                    for (BluetoothDevice bonddevice : devices) {
                        mPeiDuiList.add(bonddevice);
                        peiDuiAdapter.notifyDataSetChanged();
                    }
    
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    //判断未配对的设备
                    if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                        mFuJinList.add(device);
                        fuJinAdapter.notifyDataSetChanged();
                    }
                } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                    textView2.setText("搜索完成...");
                }
            }
        };

    我这边是创建了两个集合 分别把已配对的和附近的储存进去

     private ArrayList<BluetoothDevice> mPeiDuiList = new ArrayList<>();
        private ArrayList<BluetoothDevice> mFuJinList = new ArrayList<>();

     接下来把获取到的数据放入适配器显示 这步我就不写了 大家都会

    这里用到了蓝牙设备BluetoothDevice

    BluetoothDevice用于指代某个蓝牙设备,通常表示对方设备

    • getName:获得该设备的名称; 
    • getAddress:获得该设备的地址; 
    • createRfcommSocketToServiceRecord:根据UUID创建并返回一个BluetoothSocket。

    1 配对

    两个设备建立连接以后,就可以进行一个配对的过程。

    配对进行的时候,会产生一个密钥用来加密与鉴别连接。一个典型的情况是,从机设备会要求主机设备提供一个密码来完成一个配对过程。

    这个密码可以是固定的例如“000000”,也可以是提供给上层的随机产生的一个值。当主机设备发送一个正确的密码是,这两个设备就会相互交换密钥来加密并鉴别连接。

    2 绑定

    很多情况下,两个设备会需要经常性的断开连接,连接这样的过程,BLE有一个安全功能,允许两台设备配对的时候给对方一个长期的一套安全密钥。
    这种机制称作为绑定。允许两个设备再次连接时不需要再次进行配对就能从新加密与鉴别,只要他们存储了这个安全密钥。

    配对:

    主要写配对和通信 绑定先不多做介绍

    //点击附近的开始配对
                    mBluetoothAdapter.cancelDiscovery();//停止搜索
                    String address = mFuJinList.get(position).getAddress();
                    Toast.makeText(MainActivity.this, mFuJinList.get(position).getName() + "配对中。。。", Toast.LENGTH_SHORT).show();
                    
                    try {
                        //调用工具类与设备配对
                        //已知自己的地址 点击获取对方的地址 配对
                        remoteDevice = mBluetoothAdapter.getRemoteDevice(address);//通过mac地址获取蓝牙设备
                        ClsUtils.createBond(remoteDevice.getClass(), remoteDevice);
                    } catch (Exception e) {
                        e.printStackTrace();
                      
                    }

    这里用到了一个配对工具类ClsUtils

    package com.example.lin.mylanya.utils;
    
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    import android.bluetooth.BluetoothDevice;
    import android.util.Log;
    
    public class ClsUtils {
    
        /**
         * 与设备配对 参考源码:platform/packages/apps/Settings.git
         * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
         */
        static public boolean createBond(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
         */
        static public boolean removeBond(Class btClass,BluetoothDevice btDevice) throws Exception {
            Method removeBondMethod = btClass.getMethod("removeBond");
            Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
            return returnValue.booleanValue();
        }
    
    }

     


    通信:

    通信需要创建客户端和服务器来建立连接

    通讯是耗时操作所以都用另建了一个类继承Thread来写了

    先创建服务器:

    服务器一般是在打开蓝牙后创建  这里的ChatController是通信的工具类 这个方法里面调用了服务器线程(AccepThread)的启动方法

     
                    if (!mBluetoothAdapter.isEnabled()) {//判断蓝牙是否打开
                        mBluetoothAdapter.enable();//打开蓝牙
                    }
                    ChatController.getInstance().waitingForFriends(mBluetoothAdapter, handler);//等待客户端来连接
    •  服务器端建立套接字,等待客户端连接,
    • 调用BluetoothAdapter的listenUsingRfcommWithServiceRecord方法,产生一个BluetoothServerSocket对象,
    • 然后调用BluetoothServerSocket对象的accept方法,
    • 注意accept方法会产生阻塞,直到一个客户端连接建立,所以服务器端的socket的建立需要在一个子线程中去做,

    AccepThread

    package com.example.lin.mylanya.utils;
    
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothServerSocket;
    import android.bluetooth.BluetoothSocket;
    import android.os.Handler;
    
    import com.example.lin.mylanya.Constant;
    
    import java.io.IOException;
    import java.util.UUID;
    
    public class AccepThread extends Thread {
    
        /** 连接的名称*/
        private static final String NAME = "BluetoothClass";
        /** UUID*/
        private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
        /** 服务端蓝牙Sokcet*/
        private final BluetoothServerSocket mmServerSocket;
        private final BluetoothAdapter mBluetoothAdapter;
        /** 线程中通信的更新UI的Handler*/
        private final Handler mHandler;
        /** 监听到有客户端连接,新建一个线程单独处理,不然在此线程中会堵塞*/
        private ConnectedThread mConnectedThread;
    
        public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException {
            mBluetoothAdapter = adapter;
            this.mHandler = handler;
    
            // 获取服务端蓝牙socket
            mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        }
    
        @Override
        public void run() {
            super.run();
            // 连接的客户端soacket
            BluetoothSocket socket = null;
            // 服务端是不退出的,要一直监听连接进来的客户端,所以是死循环
            while (true){
               
                try {
                    // 获取连接的客户端socket
                    socket =  mmServerSocket.accept();
                } catch (IOException e) {
                    // 通知主线程更新UI, 获取异常
                    mHandler.sendEmptyMessage(Constant.MSG_ERROR);
                    e.printStackTrace();
                    // 服务端退出一直监听线程
                    break;
                }
    
                if(socket != null) {
                    // 管理连接的客户端socket
                    manageConnectSocket(socket);
                }
            }
        }
    
        /**
         * 管理连接的客户端socket
         * @param socket
         */
        private void manageConnectSocket(BluetoothSocket socket) {
            // 只支持同时处理一个连接
            // mConnectedThread不为空,踢掉之前的客户端
            if(mConnectedThread != null) {
                mConnectedThread.cancle();
            }
    
            // 新建一个线程,处理客户端发来的数据
            mConnectedThread = new ConnectedThread(socket, mHandler);
            mConnectedThread.start();
        }
    
        /**
         * 断开服务端,结束监听
         */
        public void cancle() {
            try {
                mmServerSocket.close();
                mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发送数据
         * @param data
         */
        public void sendData(byte[] data){
            if(mConnectedThread != null) {
                mConnectedThread.write(data);
            }
        }
    }
    

    蓝牙服务器套接字BluetoothServiceSocket

    • BluetoothServiceSocket是服务端的Socket,用来接收客户端的Socket连接请求
    • accept:监听外部的蓝牙连接请求; 
    • close:关闭服务端的蓝牙监听。
    • 当远端设备连接到了的时候,Blueboothserversocket 类将会返回一个 bluetoothsocket。


    客户端:

    客户端是在点击连接时调用  同上 是不过是调用了客户端线程的启动方法

      //点击连接
                    String address = mPeiDuiList.get(position).getAddress();
                    String name = mPeiDuiList.get(position).getName();
                    Toast.makeText(MainActivity.this, name + "开始连接 请稍后。。。", Toast.LENGTH_SHORT).show();
                    remoteDevice = mBluetoothAdapter.getRemoteDevice(address);
    
                    ChatController.getInstance().startChatWith(remoteDevice, mBluetoothAdapter, handler);//与服务器连接进行聊天 也就是客户端连接服务端
    •  客户端去连接服务器端,需要先持有服务器端的BluetoothDevice对象,
    • 先调用BluetoothDevice的createRfcommSocketToServiceRecord方法,这个方法会产生一个客户端的BluetoothSocket对象,
    • 然后调用该对象的connect方法,该过程最好也是单独起一个线程去做
    • 创建客户端需要一个UUID
    package com.example.lin.mylanya.utils;
    
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothSocket;
    import android.os.Handler;
    
    import com.example.lin.mylanya.Constant;
    import com.example.lin.mylanya.utils.ConnectedThread;
    
    import java.io.IOException;
    import java.util.UUID;
    
    public class ConnectThread extends Thread{
        private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
        /** 客户端socket*/
        private final BluetoothSocket mmSoket;
        /** 要连接的设备*/
        private final BluetoothDevice mmDevice;
        private BluetoothAdapter mBluetoothAdapter;
        /** 主线程通信的Handler*/
        private final Handler mHandler;
        /** 发送和接收数据的处理类*/
        private ConnectedThread mConnectedThread;
    
        public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {
            mmDevice = device;
            mBluetoothAdapter = bluetoothAdapter;
            mHandler = mUIhandler;
    
            BluetoothSocket tmp = null;
            try {
                // 创建客户端Socket
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                e.printStackTrace();
            }
            mmSoket = tmp;
        }
    
        @Override
        public void run() {
            super.run();
            // 关闭正在发现设备.(如果此时又在查找设备,又在发送数据,会有冲突,影响传输效率)
            mBluetoothAdapter.cancelDiscovery();
            try {
                // 连接服务器
                mmSoket.connect();
            } catch (IOException e) {
                // 连接异常就关闭
                try {
                    mmSoket.close();
                } catch (IOException e1) {
                }
                return;
            }
    
            manageConnectedSocket(mmSoket);
        }
    
        private void manageConnectedSocket(BluetoothSocket mmSoket) {
           
            // 新建一个线程进行通讯,不然会发现线程堵塞
            mConnectedThread = new ConnectedThread(mmSoket,mHandler);
            mConnectedThread.start();
        }
    
        /**
         * 关闭当前客户端
         */
        public void cancle() {
            try {
                mmSoket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发送数据
         * @param data
         */
        public void sendData(byte[] data) {
            if(mConnectedThread != null) {
                mConnectedThread.write(data);
            }
        }
    }
    

    蓝牙客户端套接字BluetoothSocket

    • BluetoothSocket是客户端的Socket,用于与对方设备进行数据通信。
    • connect:建立蓝牙的socket连接; 
    • close:关闭蓝牙的socket连接; 
    • getInputStream:获取socket连接的输入流对象; 
    • getOutputStream:获取socket连接的输出流对象; 
    • getRemoteDevice:获取远程设备信息。
    • 服务器和客户端连接上后都需要新建一个线程进行通信 不然会线程阻塞
    • 发送数据需要调用BluetoothSocket的getOutputStream(),接收数据需要调用getInputStream()方法
    • String uuid = java.util.UUID.randomUUID().toString();
    • 一般在创建Socket时需要UUID作为端口的唯一性,如果两台Android设备互联,则没有什么特殊的,
    • 如果让非Android的蓝牙设备连接Android蓝牙设备,则UUID必须使用某个固定保留的UUID
    • Android中创建UUID:UUID  uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    package com.example.lin.mylanya.utils;
    
    import android.bluetooth.BluetoothSocket;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    
    import com.example.lin.mylanya.Constant;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class ConnectedThread extends Thread{
        /** 当前连接的客户端BluetoothSocket*/
        private final BluetoothSocket mmSokcet;
        /** 读取数据流*/
        private final InputStream mmInputStream;
        /** 发送数据流*/
        private final OutputStream mmOutputStream;
        /** 与主线程通信Handler*/
        private Handler mHandler;
        private String TAG = "ConnectedThread";
    
        public ConnectedThread(BluetoothSocket socket,Handler handler) {
            mmSokcet = socket;
            mHandler = handler;
    
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            mmInputStream = tmpIn;
            mmOutputStream = tmpOut;
        }
    
        @Override
        public void run() {
            super.run();
            byte[] buffer = new byte[1024];
    
            while (true) {
                try {
                    // 读取数据
                    int bytes = mmInputStream.read(buffer);
    
                    if(bytes > 0) {
                        String data = new String(buffer,0,bytes,"utf-8");
                        // 把数据发送到主线程, 此处还可以用广播
                        Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data);
                        mHandler.sendMessage(message);
                    }
                    Log.d(TAG, "messge size :" + bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        // 踢掉当前客户端
        public void cancle() {
            try {
                mmSokcet.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 服务端发送数据
         * @param data
         */
        public void write(byte[] data) {
            try {
                mmOutputStream.write(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    ChatController

    package com.example.lin.mylanya.utils;
    
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.os.Handler;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.ProtocolFamily;
    
    public class ChatController {
        /**
         * 客户端的线程
         */
        private ConnectThread mConnectThread;
        /**
         * 服务端的线程
         */
        private AccepThread mAccepThread;
        private ChatProtocol mProtocol = new ChatProtocol();
    
        /**
         * 网络协议的处理函数
         */
        private class ChatProtocol {
            private static final String CHARSET_NAME = "utf-8";
    
            /**
             * 封包(发送数据)
             * 把发送的数据变成  数组 2进制流
             */
            public byte[] encodePackge(String data) {
                if (data == null) {
                    return new byte[0];
                } else {
                    try {
                        return data.getBytes(CHARSET_NAME);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        return new byte[0];
                    }
                }
            }
    
            /**
             * 解包(接收处理数据)
             * 把网络上数据变成自己想要的数据体
             */
            public String decodePackage(byte[] netData) {
                if (netData == null) {
                    return "";
                } else {
                    try {
                        return new String(netData, CHARSET_NAME);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        return "";
                    }
                }
            }
    
    
        }
    
        /**
         * 与服务器连接进行聊天
         */
        public void startChatWith(BluetoothDevice device, BluetoothAdapter adapter, Handler handler) {
            mConnectThread = new ConnectThread(device, adapter, handler);
            mConnectThread.start();
        }
    
        /**
         * 等待客户端来连接
         * handler : 用来跟主线程通信,更新UI用的
         */
        public void waitingForFriends(BluetoothAdapter adapter, Handler handler) {
            try {
                mAccepThread = new AccepThread(adapter, handler);
                mAccepThread.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发出消息
         */
        public void sendMessage(String msg) {
            // 封包
            byte[] data = mProtocol.encodePackge(msg);
    
            if (mConnectThread != null) {
                mConnectThread.sendData(data);
            } else if (mAccepThread != null) {
                mAccepThread.sendData(data);
            }
        }
    
        /**
         * 网络数据解码
         */
        public String decodeMessage(byte[] data){
            return mProtocol.decodePackage(data);
        }
    
        /**
         * 停止聊天
         */
        public void stopChart(){
            if(mConnectThread != null) {
                mConnectThread.cancle();
            }
            if(mAccepThread != null) {
                mAccepThread.cancle();
            }
        }
    
    
        /**
         * 以下是单例写法
         */
        private static class ChatControlHolder{
            private static ChatController mInstance = new ChatController();
        }
    
        public static ChatController getInstance(){
            return ChatControlHolder.mInstance;
        }
    
    }
    

    发送数据

     //客户端向服务器发送数据
                    String s = mEdit.getText().toString();
                    ChatController.getInstance().sendMessage(s);//发出消息

    handler接收数据

     private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                   
                    case MSG_GOT_DATA:
                        //接收消息
                        String data = (String) msg.obj;
                        Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        };

    最后销毁:

    @Override
        protected void onDestroy() {
            super.onDestroy();
            unregisterReceiver(receiver);//关闭服务
            mBluetoothAdapter.disable();//关闭蓝牙
            ChatController.getInstance().stopChart();//停止聊天
        }

     

     

     

     

     

     

     

     

    展开全文
  • 无线通信方案,有三种方案可以实施: 1、NFC 2、蓝牙 3、WIFI 下面是对这三个知识点做的一个总结,参照对比可以选择合适的方案。而本章着重讲的蓝牙之间通信。 首先介绍一下蓝牙的两个广播Receiver。 第一个:...

    无线通信方案,有三种方案可以实施:
    1、NFC 2、蓝牙 3、WIFI
    下面是对这三个知识点做的一个总结,参照对比可以选择合适的方案。而本章着重讲的蓝牙之间通信。
    这里写图片描述

    首先介绍一下蓝牙的两个广播Receiver。
    第一个:蓝牙状态的改变是通过广播接收到的。

     // 注册蓝牙状态接收广播
      IntentFilter intentFilter = new  ntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
       registerReceiver(mReceiverBluetoothStatus,intentFilter);
    
        /**
         * 定义接收蓝牙状态广播receiver
         *
         * @param savedInstanceState
         */
        private BroadcastReceiver mReceiverBluetoothStatus = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context,Intent intent) {
                int status = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
                switch (status) {
                    case BluetoothAdapter.STATE_OFF:
                        Log.d(TAG,"蓝牙已关闭");
                        break;
                    case BluetoothAdapter.STATE_ON:
                        Log.d(TAG,"蓝牙已打开");
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        Log.d(TAG,"蓝牙关闭中...");
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        Log.d(TAG,"蓝牙打开中...");
                        break;
                    default:
    
                        break;
                }
            }
        };

    第二个:蓝牙搜索到设备、绑定设备(配对)也是通过广播接收的。(搜索到设备系统会自动发一个广播)

       // 注册蓝牙device接收广播
            IntentFilter intentFilterDevice = new IntentFilter();
            // 开始查找
            intentFilterDevice.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
            // 结束查找
            intentFilterDevice.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            // 查找设备(查找到设备)
            intentFilterDevice.addAction(BluetoothDevice.ACTION_FOUND);
            // 设备扫描模式改变 (自己状态的改变action,当设置可见或者不见时都会发送此广播)
            intentFilterDevice.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
            // 绑定状态
            intentFilterDevice.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
            registerReceiver(mReceiverDeceiver,intentFilterDevice);
      /**
         * 定义接收蓝牙device广播Receiver
         */
        private BroadcastReceiver mReceiverDeceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context,Intent intent) {
                String action = intent.getAction();
                if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
                    // 开始搜索        ——接收广播
                    Log.d(TAG,"开始搜索");
                    mList.clear();
                    mAdapter.refresh(mList);
    
                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                    // 查找到设备完成   —— 接收广播
                    Log.d(TAG,"查找到设备完成");
    
                } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    // 搜索到设备       —— 接收广播
                    Log.d(TAG,"搜索到设备");
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    mList.add(device);
                    mAdapter.refresh(mList);
    
    
                } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
                    // 当自己设备设置蓝牙可见时或者不可见时 —— 接收广播
                    int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,0);
                    // 可见时
                    if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
                        Log.d(TAG,"设备可见监听");
                    } else {
                        Log.d(TAG,"设备不可见监听");
                    }
    
                } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                    // 绑定状态改变回调
                    BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    if (remoteDevice == null) {
                        Log.d(TAG,"没有绑定设备");
                        return;
                    }
    
                    int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
                    if (status == BluetoothDevice.BOND_BONDED) {
                        Log.d(TAG,"绑定设备完成: " + remoteDevice.getName());
                    } else if (status == BluetoothDevice.BOND_BONDING) {
                        Log.d(TAG,"绑定设备中: " + remoteDevice.getName());
                    } else if (status == BluetoothDevice.BOND_NONE) {
                        Log.d(TAG,"取消绑定: ");
                    }
                }
            }
    
        };

    以上基本上就是蓝牙接收状态和搜索到设备,绑定设备一系列改变的操作!基本上已经很全了。下面介绍一个蓝牙通信的流程。

    左为客户端Socket连接的一个流程:首先获取一个客户端Socket(),然后连接上,就可以读取数据和发送数据了。

    右为服务端Socket操作流程:

    蓝牙通信原理介绍:
    蓝牙通信和socket通信原理基本上是一致的,下面我给大家上一张图(图为Socket通信图)。分析一下。
    这里写图片描述

    蓝牙客户端Socket的与Sokcet流程是一样的,只不过参数不同而已。如下:
    1、创建客户端蓝牙Sokcet
    2、创建连接
    3、读写数据
    4、关闭

    服务端socket:
    1、创建服务端蓝牙Socket
    2、绑定端口号(蓝牙忽略)
    3、创建监听listen(蓝牙忽略, 蓝牙没有此监听,而是通过whlie(true)死循环来一直监听的)
    4、通过accept(),如果有客户端连接,会创建一个新的Socket,体现出并发性,可以同时与多个socket通讯)
    5、读写数据
    6、关闭

    下面看客户端代码:

    /**
     * <p>Title: ConnectThread</p >
     * <p>Description: 客户端逻辑: 客户端的线程,处理客户端socket</p >
     * <p>Company: ihaveu</p >
     *
     * @author MaWei
     * @date 2017/12/26
     */
    public class ConnectThread extends Thread{
        private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
        /** 客户端socket*/
        private final BluetoothSocket mmSoket;
        /** 要连接的设备*/
        private final BluetoothDevice mmDevice;
        private BluetoothAdapter mBluetoothAdapter;
        /** 主线程通信的Handler*/
        private final Handler mHandler;
        /** 发送和接收数据的处理类*/
        private ConnectedThread mConnectedThread;
    
        public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {
            mmDevice = device;
            mBluetoothAdapter = bluetoothAdapter;
            mHandler = mUIhandler;
    
            BluetoothSocket tmp = null;
            try {
                // 创建客户端Socket
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            mmSoket = tmp;
        }
    
        @Override
        public void run() {
            super.run();
            // 关闭正在发现设备.(如果此时又在查找设备,又在发送数据,会有冲突,影响传输效率)
            mBluetoothAdapter.cancelDiscovery();
    
            try {
                // 连接服务器
                mmSoket.connect();
            } catch (IOException e) {
                // 连接异常就关闭
                try {
                    mmSoket.close();
                } catch (IOException e1) {
                }
                return;
            }
    
            manageConnectedSocket(mmSoket);
        }
    
        private void manageConnectedSocket(BluetoothSocket mmSoket) {
            // 通知主线程连接上了服务端socket,更新UI
            mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);
            // 新建一个线程进行通讯,不然会发现线程堵塞
            mConnectedThread = new ConnectedThread(mmSoket,mHandler);
            mConnectedThread.start();
        }
    
        /**
         * 关闭当前客户端
         */
        public void cancle() {
            try {
                mmSoket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发送数据
         * @param data
         */
        public void sendData(byte[] data) {
            if(mConnectedThread != null) {
                mConnectedThread.write(data);
            }
        }
    }

    服务端代码:

    /**
     * <p>Title: AccepThread</p >
     * <p>Description: 服务端Socket通过accept()一直监听客户端连接的线程</p >
     * <p>Company: ihaveu</p >
     *
     * @author MaWei
     * @date 2017/12/26
     */
    public class AccepThread extends Thread {
    
        /** 连接的名称*/
        private static final String NAME = "BluetoothClass";
        /** UUID*/
        private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
        /** 服务端蓝牙Sokcet*/
        private final BluetoothServerSocket mmServerSocket;
        private final BluetoothAdapter mBluetoothAdapter;
        /** 线程中通信的更新UI的Handler*/
        private final Handler mHandler;
        /** 监听到有客户端连接,新建一个线程单独处理,不然在此线程中会堵塞*/
        private ConnectedThread mConnectedThread;
    
        public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException {
            mBluetoothAdapter = adapter;
            this.mHandler = handler;
    
            // 获取服务端蓝牙socket
            mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        }
    
        @Override
        public void run() {
            super.run();
            // 连接的客户端soacket
            BluetoothSocket socket = null;
    
            // 服务端是不退出的,要一直监听连接进来的客户端,所以是死循环
            while (true){
                // 通知主线程更新UI,客户端开始监听
                mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);
                try {
                    // 获取连接的客户端socket
                    socket =  mmServerSocket.accept();
                } catch (IOException e) {
                    // 通知主线程更新UI, 获取异常
                    mHandler.sendEmptyMessage(Constant.MSG_ERROR);
                    e.printStackTrace();
                    // 服务端退出一直监听线程
                    break;
                }
    
                if(socket != null) {
                    // 管理连接的客户端socket
                    manageConnectSocket(socket);
    
                    // 这里应该是手动断开,案例应该是只保证连接一个客户端,所以连接完以后,关闭了服务端socket
    //                try {
    //                    mmServerSocket.close();
    //                    mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
    //                } catch (IOException e) {
    //                    e.printStackTrace();
    //                }
                }
            }
        }
    
        /**
         * 管理连接的客户端socket
         * @param socket
         */
        private void manageConnectSocket(BluetoothSocket socket) {
            // 只支持同时处理一个连接
            // mConnectedThread不为空,踢掉之前的客户端
            if(mConnectedThread != null) {
                mConnectedThread.cancle();
            }
    
            // 主线程更新UI,连接到了一个客户端
            mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);
            // 新建一个线程,处理客户端发来的数据
            mConnectedThread = new ConnectedThread(socket, mHandler);
            mConnectedThread.start();
        }
    
        /**
         * 断开服务端,结束监听
         */
        public void cancle() {
            try {
                mmServerSocket.close();
                mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发送数据
         * @param data
         */
         public void sendData(byte[] data){
             if(mConnectedThread != null) {
                 mConnectedThread.write(data);
             }
         }
    }

    下面看一个共同通讯处理类:

    /**
     * <p>Title: ConnectedThread</p >
     * <p>Description: 客户端和服务端 处理 发送数据 和获取数据</p >
     * <p>Company: ihaveu</p >
     *
     * @author MaWei
     * @date 2017/12/26
     */
    public class ConnectedThread extends Thread{
        /** 当前连接的客户端BluetoothSocket*/
        private final BluetoothSocket mmSokcet;
        /** 读取数据流*/
        private final InputStream mmInputStream;
        /** 发送数据流*/
        private final OutputStream mmOutputStream;
        /** 与主线程通信Handler*/
        private Handler mHandler;
        private String TAG = "ConnectedThread";
    
        public ConnectedThread(BluetoothSocket socket,Handler handler) {
            mmSokcet = socket;
            mHandler = handler;
    
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            mmInputStream = tmpIn;
            mmOutputStream = tmpOut;
        }
    
        @Override
        public void run() {
            super.run();
            byte[] buffer = new byte[1024];
    
            while (true) {
                try {
                    // 读取数据
                    int bytes = mmInputStream.read(buffer);
    
                    if(bytes > 0) {
                        String data = new String(buffer,0,bytes,"utf-8");
                        // 把数据发送到主线程, 此处还可以用广播
                        Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data);
                        mHandler.sendMessage(message);
                    }
    
                    Log.d(TAG, "messge size :" + bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        // 踢掉当前客户端
        public void cancle() {
            try {
                mmSokcet.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 服务端发送数据
         * @param data
         */
        public void write(byte[] data) {
            try {
                mmOutputStream.write(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    下面是自己写的一个聊天demo

    /**
     * <p>Title: ChatController</p >
     * <p>Description: 聊天控制器</p >
     * <p>Company: ihaveu</p >
     *
     * @author MaWei
     * @date 2017/12/26
     */
    public class ChatController {
        /** 客户端的线程*/
        private ConnectThread mConnectThread;
        /** 服务端的线程*/
        private AccepThread mAccepThread;
        private ChatProtocol mProtocol = new ChatProtocol();
    
        /**
         * 网络协议的处理函数
         */
        private class ChatProtocol implements ProtocoHandler<String>{
            private static final String CHARSET_NAME = "utf-8";
    
            /**
             * 封包(发送数据)
             * 把发送的数据变成  数组 2进制流
             */
            @Override
            public byte[] encodePackge(String data) {
                if(data == null) {
                    return new byte[0];
                }else {
                    try {
                        return data.getBytes(CHARSET_NAME);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        return new byte[0];
                    }
                }
            }
    
            /**
             * 解包(接收处理数据)
             * 把网络上数据变成自己想要的数据体
             */
            @Override
            public String decodePackage(byte[] netData) {
                if(netData == null) {
                    return "";
                }else {
                    try {
                        return new String(netData, CHARSET_NAME);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        return "";
                    }
                }
            }
        }
    
        /**
         * 与服务器连接进行聊天
         */
        public void startChatWith(BluetoothDevice device,BluetoothAdapter adapter,Handler handler){
            mConnectThread = new ConnectThread(device, adapter, handler);
            mConnectThread.start();
        }
    
        /**
         * 等待客户端来连接
         * handler : 用来跟主线程通信,更新UI用的
         */
        public void waitingForFriends(BluetoothAdapter adapter, Handler handler) {
            try {
                mAccepThread = new AccepThread(adapter,handler);
                mAccepThread.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发出消息
         */
        public void sendMessage(String msg){
            // 封包
            byte[] data = mProtocol.encodePackge(msg);
    
            if(mConnectThread != null) {
                mConnectThread.sendData(data);
            }else if(mAccepThread != null) {
                mAccepThread.sendData(data);
            }
    
        }
    
        /**
         * 网络数据解码
         */
        public String decodeMessage(byte[] data){
            return mProtocol.decodePackage(data);
        }
    
        /**
         * 停止聊天
         */
        public void stopChart(){
            if(mConnectThread != null) {
                mConnectThread.cancle();
            }
            if(mAccepThread != null) {
                mAccepThread.cancle();
            }
        }
    
    
        /**
         * 以下是单例写法
         */
        private static class ChatControlHolder{
            private static ChatController mInstance = new ChatController();
        }
    
        public static ChatController getInstance(){
            return ChatControlHolder.mInstance;
        }
    }

    OK,完成,大家主要看上面的广播,和蓝牙sokcet流程,其他的根据自己业务要求写就OK

    展开全文
  • 经典蓝牙的连接通信

    2018-02-23 11:17:29
    最近需要做一个从车载电脑里面读取汽车的发动机相关信息的工作就是所谓的OBD信息采集,这些信息 通过经典蓝牙传输到目标设备上。与现在的免握手的蓝牙连接相比还是有很多不同,所以在这里mark一下,希望能帮助有需要...

    最近需要做一个从车载电脑里面读取汽车的发动机相关信息的工作就是所谓的OBD信息采集,这些信息 通过经典蓝牙传输到目标设备上。与现在的免握手的蓝牙连接相比还是有很多不同,所以在这里mark一下,希望能帮助有需要的朋友。

    扫面蓝牙
    首先获得蓝牙权限,然后打开蓝牙,设置蓝牙扫面结果监听然后开启蓝牙扫描

    OBDBlueDevice obdBlueDevice=new OBDBlueDevice(this,"此处输入mac地址,如果没有就为空");
    obdBlueDevice.NoticeRefreshData(new NoticeRefresh() {
        @Override
        public void noticeRefresh(ArrayList<BluetoothDevice> devices) {
             if(devices.size()>0){
                  if(adapter==null){
                       progressBar.setVisibility(View.GONE);
                       //添加蓝牙扫面得到的结果到列表适配器中
                       adapter=new ListAdapter(devices,sdkActivity.this);
                       listView.setVisibility(View.VISIBLE);
                       listView.setAdapter(adapter);
                       //选中的蓝牙连接
                       adapter.mOnItemClick(new ListAdapter.OnItemClick() {
                            @Override
                            public void onItemClick(BluetoothDevice device) {
                                    obdBlueDevice.setConnectToItem(device);
                                }
                            });
                        }
                }else {
                    progressBar.setVisibility(View.GONE);
                    Toast.makeText(getApplicationContext(),"没有发现设备",Toast.LENGTH_SHORT).show();
                    }
                }
            });
    

    通过OBDBlueDevice类的方法开启数据的监听,刷新和选中蓝牙对象的连接操作。

    下面对封装的OBDBlueDevice的解析
    初始化OBDBlueDevice

    //初始化device,拿到默认的bluetoothAdapter
    public OBDBlueDevice(Context context,String mac) {
       this.context=context;
       this.mac=mac;
       this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    然后就可以开始工作了,这里用了Rxjava的方式开启蓝牙扫描

    
    Consumer<Boolean> DeviceObserver=new Consumer<Boolean>() {
            @Override
            public void accept(Boolean aBoolean) throws Exception {
                if(aBoolean){
                    //开始搜索蓝牙设备
                    SearchBlueToothDevice();
                }
            }
        };
    
    private Observable<Boolean> DeviceObservable=Observable.create(new ObservableOnSubscribe<Boolean>() {
            @Override
            public void subscribe(ObservableEmitter<Boolean> emitter) throws Exception {
                if(bluetoothAdapter==null){
                    emitter.onNext(false);
                    emitter.onComplete();
                    throw new NoBlueException("该设备不支持蓝牙");
                }else {
                    if(!bluetoothAdapter.isEnabled()){
                        emitter.onNext(false);
                        emitter.onComplete();
                        Toast.makeText(context.getApplicationContext(),"请开启蓝牙",Toast.LENGTH_SHORT).show();
                    }else {
                        emitter.onNext(true);
                        emitter.onComplete();
                    }
                }
            }
        });
    
    
     private void SearchBlueToothDevice() {
            Log.e(TAG,"开始搜索");
            //这里扫描得到的蓝牙结果会通过广播传送
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if(bluetoothAdapter.isDiscovering()){
                        bluetoothAdapter.cancelDiscovery();
                        bluetoothAdapter.startDiscovery();
                    }else {
                        bluetoothAdapter.startDiscovery();
                    }
                }
            }).start();
        }
    //开启蓝牙扫描的方法
    public void starWork() {
        //注册蓝牙扫面广播接收器
       context.registerReceiver(mreceiver,getIntentFileter());
       //查找蓝牙设备
       DeviceObservable.subscribe(DeviceObserver);
    }
    
    //创建广播Intent
    private IntentFilter getIntentFileter(){
        IntentFilter intentFilter=new IntentFilter();
        //添加广播监听事件
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        return intentFilter;
    }
    
    private final BroadcastReceiver mreceiver=new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action=intent.getAction();
            if(BluetoothDevice.ACTION_FOUND.equals(action)){
               //发现蓝牙,并从Intent中获取BluetoothDevice对象
               BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    Log.e(TAG,"发现设备"+device.getAddress());
                    //去重
                    if(!bluelist.contains(device)){
                        bluelist.add(device);
                    }
           }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
                    //发现设备完成,返回并通知,开始连接
                    Log.e(TAG,"扫描完成");
                    if(bluelist.size()>0){
                        //通知刷新列表
                        if(noticeRefresh!=null){
                            noticeRefresh.noticeRefresh(bluelist);
                        }
                        //设置扫描完成标志
                        isFinish=true;
                    }else {
                        Toast.makeText(OBDBlueDevice.this.context.getApplicationContext(),"没有发现蓝牙设备",Toast.LENGTH_SHORT).show();
                    }
                }
            }
        };
    

    下面开始蓝牙的连接,当点击想要连接的蓝牙,会弹出一个系统的对话框,要求输入连接的配对密码,密码正确就可以获取蓝牙的输入输出流了。

    //蓝牙连接
    public void setConnectToItem(BluetoothDevice itemDevice){
            BluetoothClientThread connectThread=new BluetoothClientThread(itemDevice);
            new Thread(connectThread).start();
        }
    
     class BluetoothClientThread implements Runnable {
            public BluetoothClientThread(BluetoothDevice device) {
                if(bluetoothAdapter.isEnabled()){
                    //获得蓝牙Socket
                    BluetoothSocket tmp=null;
                    try {
                        tmp=device.createRfcommSocketToServiceRecord(mUUID);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    bluetoothSocket=tmp;
    
                }
            }
    
            @Override
            public void run() {
                if(bluetoothAdapter.isDiscovering()){
                    bluetoothAdapter.cancelDiscovery();
                }
                if(inputStream==null||outputStream==null){
                    try {
                        //连接蓝牙设备,获取输入输出流信息
                        OBDBlueDevice.this.bluetoothSocket.connect();                      OBDBlueDevice.this.inputStream=bluetoothSocket.getInputStream();                   OBDBlueDevice.this.outputStream=bluetoothSocket.getOutputStream();
                        isConnect=true;
                        Log.e(TAG,"socket连接成功");
                    } catch (IOException e) {
                        Log.e(TAG,"socket连接失败");
                        isConnect=false;
                        e.printStackTrace();
                    }
                }
                //如果蓝牙连接成功,就可以开始传输数据了
                if(isConnect()){
                    //通过流传输你想传输的数据
                }
    
            }
        }
    展开全文
  • 蓝牙通信

    2020-06-01 22:47:48
    .xml文件: <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width=...

    .xml文件:

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/title_left_text"
                style="?android:attr/windowTitleStyle"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_alignParentLeft="true"
                android:layout_weight="1"
                android:gravity="left"
                android:ellipsize="end"
                android:singleLine="true" />
            <TextView
                android:id="@+id/title_right_text"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_alignParentRight="true"
                android:layout_weight="1"
                android:ellipsize="end"
                android:gravity="right"
                android:singleLine="true"
                android:textColor="#fff" />
        </LinearLayout>
    </androidx.appcompat.widget.Toolbar>
    
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/scan"
            android:icon="@android:drawable/ic_menu_myplaces"
            android:title="@string/connect" />
        <item android:id="@+id/discoverable"
            android:icon="@android:drawable/ic_menu_view"
            android:title="@string/discoverable" />
        <item android:id="@+id/back"
            android:icon="@android:drawable/ic_menu_close_clear_cancel"
            android:title="@string/back" />
    </menu>
    

    .java文件

    // 创建监听线程,准备接受新连接。使用阻塞方式,调用 BluetoothServerSocket.accept()
        private class AcceptThread extends Thread {
            private final BluetoothServerSocket mmServerSocket;
    
            public AcceptThread() {
                BluetoothServerSocket tmp = null;
                try {
                    //使用射频端口(RF comm)监听
                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
                } catch (IOException e) {
                }
                mmServerSocket = tmp;
            }
    
            @Override
            public void run() {
                setName("AcceptThread");
                BluetoothSocket socket = null;
                while (mState != STATE_CONNECTED) {
                    try {
                        socket = mmServerSocket.accept();
                    } catch (IOException e) {
                        break;
                    }
                    if (socket != null) {
                        synchronized (ChatService.this) {
                            switch (mState) {
                                case STATE_LISTEN:
                                case STATE_CONNECTING:
                                    connected(socket, socket.getRemoteDevice());
                                    break;
                                case STATE_NONE:
                                case STATE_CONNECTED:
                                    try {
                                        socket.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                    break;
                            }
                        }
                    }
                }
            }
    
            public void cancel() {
                try {
                    mmServerSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /*
            连接线程,专门用来对外发出连接对方蓝牙的请求和处理流程。
            构造函数里通过 BluetoothDevice.createRfcommSocketToServiceRecord() ,
            从待连接的 device 产生 BluetoothSocket. 然后在 run 方法中 connect ,
            成功后调用 BluetoothChatSevice 的 connected() 方法。定义 cancel() 在关闭线程时能够关闭相关socket 。
         */
        private class ConnectThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final BluetoothDevice mmDevice;
    
            public ConnectThread(BluetoothDevice device) {
                mmDevice = device;
                BluetoothSocket tmp = null;
                try {
                    tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                mmSocket = tmp;
            }
    
            @Override
            public void run() {
                setName("ConnectThread");
                mAdapter.cancelDiscovery();
                try {
                    mmSocket.connect();
                } catch (IOException e) {
                    connectionFailed();
                    try {
                        mmSocket.close();
                    } catch (IOException e2) {
                        e.printStackTrace();
                    }
                    ChatService.this.start();
                    return;
                }
                synchronized (ChatService.this) {
                    mConnectThread = null;
                }
                connected(mmSocket, mmDevice);
            }
    
            public void cancel() {
                try {
                    mmSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /*
            双方蓝牙连接后一直运行的线程;构造函数中设置输入输出流。
            run()方法中使用阻塞模式的 InputStream.read()循环读取输入流,然后发送到 UI 线程中更新聊天消息。
            本线程也提供了 write() 将聊天消息写入输出流传输至对方,传输成功后回写入 UI 线程。最后使用cancel()关闭连接的 socket
         */
        private class ConnectedThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final InputStream mmInStream;
            private final OutputStream mmOutStream;
    
            public ConnectedThread(BluetoothSocket socket) {
                mmSocket = socket;
                InputStream tmpIn = null;
                OutputStream tmpOut = null;
                try {
                    tmpIn = socket.getInputStream();
                    tmpOut = socket.getOutputStream();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                mmInStream = tmpIn;
                mmOutStream = tmpOut;
            }
    
            @Override
            public void run() {
                byte[] buffer = new byte[1024];
                int bytes;
                while (true) {
                    try {
                        bytes = mmInStream.read(buffer);
                        mHandler.obtainMessage(weixinFragment.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
                    } catch (IOException e) {
                        connectionLost();
                        break;
                    }
                }
            }
    
            public void write(byte[] buffer) {
                try {
                    mmOutStream.write(buffer);
                    mHandler.obtainMessage(weixinFragment.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            public void cancel() {
                try {
                    mmSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    

    运行截图:
    在这里插入图片描述
    在这里插入图片描述
    码云地址如下:
    https://gitee.com/snowsummer9/lanyatongxin/tree/master

    展开全文
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • 蓝牙是一种短距离通信系统,蓝牙系统分为两种不同的技术:经典蓝牙(Classic Bluetooth)和蓝牙低功耗(Bluetooth Low Energy)。 从整体结构上,蓝牙可分为控制器(Controller)和主机(Host)两大部分; ESP32应用的三...
  • 公司项目,需要开发一个,手机蓝牙通过蓝牙模块和传感器通信的软件 手机app: 我是先看了android官网关于蓝牙这方面的知识(https://developer.android.com/guide/topics/connectivity/bluetooth), 然后...
  • 欢迎使用Markdown编辑器写博客 本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 ...
  • 最近项目上需要蓝牙通讯功能,所以自己私下里学习了一下蓝牙通讯相关的知识。...1、首先是蓝牙通信机制 蓝牙通信也是采用Socket机制,通信双方有一方为服务器端,另一方为客户端,可能有 人会觉...
  • 上篇文章我们讲解了,蓝牙配对和蓝牙连接相关知识,...1.蓝牙通信简介 无论是做Java还是Android开发的朋友肯定都比较熟悉Socket的连接,在java中通信用的是Socket,同样的蓝牙之间通信(这里说的是经典蓝牙)方式...
  • 经典蓝牙通信代码篇

    2018-01-17 10:01:51
    公司一代产品用的经典蓝牙通信,特点就是基于socket连接,传输速率快,缺点就是,额,,,耗电!虽然已经被取代了?,但还是记录一下经典蓝牙通信基本原理缅怀一下吧。 好了,全军出击! 蓝牙权限 蓝牙权限不属于...
  • Windows蓝牙通信的开发

    2019-10-21 19:46:06
    周四接到关于window上的蓝牙开发项目,预定时间在五天之内结束,但是五天的时间很快过去,还是没有做...使用qt完成蓝牙通信的开发 2.具体的功能是:首先要广播自己的设备,等待被连接,连接成功后,可进行相应服务...
  • 最近项目需要做蓝牙...对于蓝牙通信方面,比如蓝牙聊天,其实跟蓝牙连接蓝牙秤差不多。如果是app连接蓝牙秤,比手机连接手机聊天少了那么一两个步骤。其实可以这么理解,手机蓝牙连接蓝牙外设备(非手机),那么就是
  • 刚好碰到这蓝牙通信方面的项目,上网也看过其他人在蓝牙这方面写的博客,但大多都不全,给一些初触蓝牙的开发者造成一定的麻烦,自己当初也费了不少劲。所以把自己参考网上的一些资料用Android studio写的代码完全放...
  • Android开发之蓝牙通信(一)Android开发之蓝牙通信(二)前两篇提到了蓝牙开发的基本使用流程以及通信框架BluetoothKit,博主的蓝牙通信项目已经迭代了几个版本了,决定最后来做一个总结,主要是蓝牙通信协议相关,...
  • 之前写过一篇Android蓝牙通信的文章,介绍了Android蓝牙的使用和一款蓝牙聊天app的原理,可以点这里看这篇文章。 这篇文章是对上篇文章的完善和补充。 原有问题 上篇文章在分析蓝牙聊天app时提到过“管理连接线程...
  • 读Android蓝牙通信源码

    2015-11-21 23:32:06
    我说的读android蓝牙通信源码并不是说,读google官方api,那个我也看了一下,里面全是英文倒不说,英文好的,直接无视掉。关键是代码写的我实在难以理解。当然,这也和我的阅读源码的能力有关联的。不过,幸好我在...
  • Android BLE 蓝牙通信

    2019-01-21 14:10:26
    简介:Android BLE 蓝牙通信库 更多:作者 提 Bug  标签:   这个库用于 Android 蓝牙 BLE 设备通信,支持设备扫描,连接,读写,通知。 这套框架存在的意义 一、统一解决 Android 蓝牙通信过程中的兼容性...
1 2 3 4 5 ... 20
收藏数 31,854
精华内容 12,741
关键字:

蓝牙通信