最近在研究手机通过蓝牙连接另一部蓝牙设备,以达到从另一部蓝牙设备上读取数据的目的。


第一步首先自定义一个发现蓝牙设备的广播接收器。

下面是详细的代码:


package com.test;

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.Handler;
import android.os.Message;

public class BlueToothSearchReceiver extends BroadcastReceiver {
    public static final int DEVICE_FOUND = 0;
    public static final int DEVICE_NOT_FOUND = 1;

    public Handler mHandler;

    public BlueToothSearchReceiver(Handler handler) {
        this.mHandler = handler;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (BluetoothDevice.ACTION_FOUND.equals(action) || BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            mHandler.sendMessage(Message.obtain(mHandler, DEVICE_FOUND, device));
        }
        else {
            mHandler.sendMessage(Message.obtain(mHandler, DEVICE_NOT_FOUND, ""));
        }
    }

    public IntentFilter getIntentFilter() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        intentFilter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);

        return intentFilter;
    }
}


第二步注册发现蓝牙设备的广播接收器


@Override
    protected void onResume() {
        super.onResume();
        mBlueToothSearchReceiver = new BlueToothSearchReceiver(mHandler);
        // 注册接收周边可见蓝牙设备的广播接收器
        registerReceiver(mBlueToothSearchReceiver, mBlueToothSearchReceiver.getIntentFilter());
    }


第三步发现蓝牙设备以后绑定到已发现的蓝牙设备列表上

第四步如果蓝牙之间已经配对,那么就进行连接操作,如果没有连接那么先进行配对操作,然后再进行连接操作,连接完成之后就等待数据读取的过程了。

 下面是详细的代码:

package com.test;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemClickListener {
    private Button btnStatus, btnSearch;
    private ListView lvDevices;
    private EditText etMessage;

    private BluetoothAdapter mBluetoothAdapter = null;

    private List<BluetoothDevice> mBluetoothDeviceList = new ArrayList<>();
    private MyAdapter mAdapter = null;

    private BlueToothSearchReceiver mBlueToothSearchReceiver;

    private static final int CONNECT_FAILURE = 2;
    private static final int CONNECT_SUCCESS = 3;
    private static final int WRITE_FAILED = 4;
    private static final int READ_FAILED = 5;
    private static final int READ_SUCCESS = 6;

    private static final String KEY_SOCKET = "socket";
    private static final String KEY_SOCKET_CONNECT = "connect";

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case BlueToothSearchReceiver.DEVICE_NOT_FOUND:
                    showMsg("没有发现已开启的蓝牙设备");
                    break;
                case BlueToothSearchReceiver.DEVICE_FOUND:
                    BluetoothDevice device = (BluetoothDevice) msg.obj;

                    if (!mBluetoothDeviceList.contains(device))
                        mBluetoothDeviceList.add(device);

                    if (mAdapter != null)
                        mAdapter.notifyDataSetChanged();
                    break;
                case CONNECT_FAILURE:
                    showMsg("连接失败");
                    break;
                case CONNECT_SUCCESS:
                    showMsg("连接成功,开始读取数据");
                    new ThreadReadData((Map<String, Object>) msg.obj).start();
                    break;
                case WRITE_FAILED:
                    showMsg("写入失败");
                    break;
                case READ_FAILED:
                    showMsg("读取失败");
                    break;
                case READ_SUCCESS:
                    showMsg("读取成功");
                    String receive = msg.obj + "";
                    String content = etMessage.getText().toString() + "\r\n" + receive;
                    etMessage.setText(content);
                    etMessage.setSelection(content.length());
                    break;
            }
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mBlueToothSearchReceiver = new BlueToothSearchReceiver(mHandler);
        // 注册接收周边可见蓝牙设备的广播接收器
        registerReceiver(mBlueToothSearchReceiver, mBlueToothSearchReceiver.getIntentFilter());
    }

    @Override
    protected void onDestroy() {
        if (mBluetoothAdapter != null) {
            mBluetoothAdapter.cancelDiscovery();
        }
        super.onDestroy();
    }

    private void initView() {
        btnStatus = (Button) findViewById(R.id.btnOpen);
        btnSearch = (Button) findViewById(R.id.btnSearch);
        lvDevices = (ListView) findViewById(R.id.lvDevices);
        etMessage = (EditText) findViewById(R.id.etMessage);

        btnStatus.setOnClickListener(this);
        btnSearch.setOnClickListener(this);
        lvDevices.setOnItemClickListener(this);

        initBluetoothStatus();
    }

    private void initBluetoothStatus() {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter == null) {
            showMsg("当前设备不存在蓝牙模块");
        } else {
            if (mBluetoothAdapter.isEnabled()) {
//                mBluetoothAdapter.disable(); // 关闭蓝牙

                btnStatus.setText("蓝牙状态:开启");
            } else {
                btnStatus.setText("蓝牙状态:关闭");

                // 无需询问用户,直接开启蓝牙,需要android.permission.BLUETOOTH_ADMIN权限
                // mBluetoothAdapter.enable();
            }
        }
    }

    private void openOrCloseBlueTooth() {
        if (mBluetoothAdapter == null) {
            showMsg("当前设备不存在蓝牙模块");
        } else {
            if (mBluetoothAdapter.isEnabled()) {
                mBluetoothAdapter.disable(); // 关闭蓝牙

                btnStatus.setText("蓝牙状态:关闭");
            } else {
                // 询问用户,如果用户同意就打开蓝牙
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); // 300就表示300秒
                startActivity(intent);

                // 无需询问用户,直接开启蓝牙,需要android.permission.BLUETOOTH_ADMIN权限
                // mBluetoothAdapter.enable();

                btnStatus.setText("蓝牙状态:开启");
            }
        }
    }

    private void bindDevices() {
        // 获取蓝牙适配器中已经配对的设备
        if (mBluetoothAdapter != null) {
            Set<BluetoothDevice> deviceList = mBluetoothAdapter.getBondedDevices();
            if (deviceList != null && deviceList.size() > 0) {
                for (BluetoothDevice device : deviceList) {
                    mBluetoothDeviceList.add(device);
                }
            }
        }

        // 显示已经绑定和可见但是还没有绑定的设备信息
        mAdapter = new MyAdapter(mBluetoothDeviceList, R.layout.activity_main_item);
        lvDevices.setAdapter(mAdapter);
    }

    private void showMsg(String msg) {
        Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnOpen:
                openOrCloseBlueTooth();
                break;
            case R.id.btnSearch:
                mBluetoothDeviceList.clear();
                if (mAdapter != null) {
                    mAdapter.notifyDataSetChanged();
                }

                if (mBluetoothAdapter != null) {
                    mBluetoothAdapter.startDiscovery();
                }

                bindDevices();
                break;
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        if (mBluetoothDeviceList != null) {
            final BluetoothDevice device = mBluetoothDeviceList.get(position);
            int conStatus = device.getBondState();
            switch (conStatus) {
                case BluetoothDevice.BOND_BONDED:
                    /*try {
                        // 解除
                        Method removeBond = device.getClass().getMethod("removeBond",  (Class<?>[]) null);
                        removeBond.invoke(device);

                        if (mAdapter != null) {
                            mAdapter.notifyDataSetChanged();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }*/

                    showMsg("连接线程开始");
                    new ThreadBluetoothConnect(device).start();
                    break;
                case BluetoothDevice.BOND_NONE:
                    try {
                        // 配对
                        Method createBond = device.getClass().getMethod("createBond", (Class<?>[]) null);
                        createBond.invoke(device);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    }

    /**
     * 适配器
     */
    class MyAdapter extends CommonAdapter<BluetoothDevice> {
        public MyAdapter(List<BluetoothDevice> datas, int itemLayoutId) {
            super(MainActivity.this, datas, itemLayoutId);
        }

        @Override
        public void convert(ViewHolder holder, BluetoothDevice item) {
            holder.setText(R.id.tvName, "设备名称:" + item.getName());
            holder.setText(R.id.tvAddress, "设备地址:" + item.getAddress());
            holder.setText(R.id.tvStatus, "匹配状态:" + getStatus(item.getBondState()));
        }

        private String getStatus(int status) {
            switch (status) {
                case BluetoothDevice.BOND_NONE:
                    return "未匹配";
                case BluetoothDevice.BOND_BONDING:
                    return "匹配中";
                case BluetoothDevice.BOND_BONDED:
                    return "已匹配";
                default:
                    return "未匹配";
            }
        }
    }

    /**
     * 蓝牙连接线程
     */
    class ThreadBluetoothConnect extends Thread {
        BluetoothDevice mDevice;

        public ThreadBluetoothConnect(BluetoothDevice device) {
            this.mDevice = device;
        }

        @Override
        public void run() {
            super.run();

            /*try {
                mBluetoothAdapter.cancelDiscovery();

                final String spp_uuid = "00001101-0000-1000-8000-00805F9B34FB";
                UUID uuid = UUID.fromString(spp_uuid);
                socket = device.createRfcommSocketToServiceRecord(uuid);
                socket.connect();

            } catch (IOException e) {
                System.err.println(e.getMessage());
            }*/

            mBluetoothAdapter.cancelDiscovery();
            BluetoothSocket tmp = null;
            Method method;
            try {
                method = mDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                tmp = (BluetoothSocket) method.invoke(mDevice, 1);
            } catch (Exception e) {
                mHandler.sendEmptyMessage(CONNECT_FAILURE);
            }

            try {
                tmp.connect();

                Map<String, Object> map = new HashMap<>();
                map.put(KEY_SOCKET, tmp);
                map.put(KEY_SOCKET_CONNECT, true);
                mHandler.sendMessage(Message.obtain(mHandler, CONNECT_SUCCESS, map));
            } catch (Exception e) {
                mHandler.sendEmptyMessage(CONNECT_FAILURE);
            }
        }
    }

    class ThreadReadData extends Thread {
        Map<String, Object> mData;

        public ThreadReadData(Map<String, Object> data) {
            this.mData = data;
        }

        @Override
        public void run() {
            super.run();

            boolean connected = (boolean) mData.get(KEY_SOCKET_CONNECT);
            BluetoothSocket socket = (BluetoothSocket) mData.get(KEY_SOCKET);

            if (connected) {
            /*try {
                OutputStream outStream = socket.getOutputStream();
                outStream.write(getHexBytes(""));
            } catch (IOException e) {
                mHandler.sendEmptyMessage(WRITE_FAILED);
            }*/

                try {
                    InputStream inputStream = socket.getInputStream();
                    String data;
                    while (true) {
                        try {
                            byte[] buffer = new byte[1024];
                            inputStream.read(buffer);

                            data = new String(buffer);
                            Message msg = mHandler.obtainMessage();
                            msg.what = READ_SUCCESS;
                            msg.obj = socket.getRemoteDevice().getName() + "    " + data;
                            mHandler.sendMessage(msg);
                        } catch (IOException e) {
                            mHandler.sendEmptyMessage(READ_FAILED);
                            e.printStackTrace();
                            break;
                        }
                    }
                } catch (IOException e) {
                    mHandler.sendEmptyMessage(WRITE_FAILED);
                    e.printStackTrace();
                }
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /** 私有方法 */
    private byte[] getHexBytes(String message) {
        int len = message.length() / 2;
        char[] chars = message.toCharArray();
        String[] hexStr = new String[len];
        byte[] bytes = new byte[len];
        for (int i = 0, j = 0; j < len; i += 2, j++) {
            hexStr[j] = "" + chars[i] + chars[i + 1];
            bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
        }
        return bytes;
    }
}



通过以上所提供的方法,就可以进行蓝牙通信了。

上文中用到的两个布局文件如下所示:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:padding="20dp">


    <Button
        android:id="@+id/btnOpen"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="蓝牙状态"/>

    <Button
        android:id="@+id/btnSearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnOpen"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:text="搜索设备"/>

    <ListView
        android:id="@+id/lvDevices"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnSearch"
        android:layout_marginTop="20dp"></ListView>

    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/lvDevices"
        android:layout_marginTop="20dp"/>
</RelativeLayout>



<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="20dp">

    <TextView
        android:id="@+id/tvName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"/>

    <TextView
        android:id="@+id/tvAddress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@id/tvName"
        android:layout_marginTop="5dp"/>

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvName"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@id/tvAddress"/>
</RelativeLayout>



该文是自己第一次写关于蓝牙方面的一些见解,如有不足之处,还请指正,谢谢!