蓝牙的基础知识我就不讲了,有兴趣可以看下我之前写的博客,现在我们要做的就是实现两个蓝牙之间的通讯,并且实现聊天功能,这里大家准备两个手机即可。

我们先按照流程走一遍,首先是验证一下设备是否支持蓝牙,一般都是支持的

if (!BtManager.getInstance().isSupport()) {
    Toast.makeText(this, "当前设备不支持蓝牙", Toast.LENGTH_SHORT).show();
    return;
}

然后验证一下是否打开了蓝牙

BtManager.getInstance().open();

我对蓝牙的相关Api是做了一些封装的,紧接着我获取到他之前配对过的设备

Set<BluetoothDevice> mBondedList = BtManager.getInstance().getBondedDevices();
for (BluetoothDevice device : mBondedList) {
   	addModel(device.getName(), device.getAddress());
}

这里要注意一点的是,我们需要添加权限

 <uses-permission android:name="android.permission.BLUETOOTH" />
 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

ACCESS_COARSE_LOCATION是后面Android6.0新需要的权限,不然搜索设备是获取不到的

    private void requestPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
            } else {
                startDiscovery();
            }
        } else {
            startDiscovery();
        }
    }
    
    private void startDiscovery() {
        BtManager.getInstance().startDiscovery();
    }

紧接着在在回调中接收结果

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        for (int i = 0; i < permissions.length; i++) {
            if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                int result = grantResults[i];
                if (result == PackageManager.PERMISSION_GRANTED) {
                    startDiscovery();
                } else {
                    requestPermission();
                }
            }
        }
    }

如果同意了此权限就直接搜索设备了,搜索设备我们会需要用到两个广播

        mBtFoundReceiver = new BtFoundReceiver();
        IntentFilter filter = new IntentFilter();
        //搜索结果
        filter.addAction(BluetoothDevice.ACTION_FOUND);
        //搜索完成
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(mBtFoundReceiver, filter);

这样我们就可以接收到他返回的列表了

    class BtFoundReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device != null) {
                    //判断是否配对过
                    if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                        addModel(device.getName(), device.getAddress());
                    }
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {

            }
        }
    }

如图

在这里插入图片描述

所以主页的全部代码如下:

package com.liuguilin.iot_bt;

import android.Manifest;
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.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.Window;
import android.widget.Toast;

import com.liuguilin.iot_bt.adapter.BtListAdapter;
import com.liuguilin.iot_bt.manager.BtManager;
import com.liuguilin.iot_bt.model.BtListModel;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * IOT系列博客 —— BT 通讯
 * 作者:刘桂林
 */
public class MainActivity extends AppCompatActivity {

    public static final String TAG = "IOT_BT";

    public static final int PERMISSION_REQUEST_COARSE_LOCATION = 1001;

    private RecyclerView mBtListRyView;
    private BtListAdapter mBtListAdapter;
    private List<BtListModel> mList = new ArrayList<>();

    private BtFoundReceiver mBtFoundReceiver;

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

        initView();
    }

    private void initView() {
        mBtListRyView = (RecyclerView) findViewById(R.id.mBtListRyView);
        mBtListRyView.setLayoutManager(new LinearLayoutManager(this));
        mBtListAdapter = new BtListAdapter(this, mList);
        mBtListRyView.setAdapter(mBtListAdapter);

        mBtListAdapter.setOnItemClickListener(new BtListAdapter.OnItemClickListener() {
            @Override
            public void OnClick(int i) {
                BtManager.getInstance().cancelDiscovery();
                Intent intent = new Intent(MainActivity.this, ChatActivity.class);
                BtListModel model = mList.get(i);
                intent.putExtra("name", model.getName());
                intent.putExtra("address", model.getAddress());
                startActivity(intent);
            }
        });

        mBtFoundReceiver = new BtFoundReceiver();
        IntentFilter filter = new IntentFilter();
        //搜索结果
        filter.addAction(BluetoothDevice.ACTION_FOUND);
        //搜索完成
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(mBtFoundReceiver, filter);

        if (!BtManager.getInstance().isSupport()) {
            Toast.makeText(this, "当前设备不支持蓝牙", Toast.LENGTH_SHORT).show();
            return;
        }

        BtManager.getInstance().open();

        Set<BluetoothDevice> mBondedList = BtManager.getInstance().getBondedDevices();
        for (BluetoothDevice device : mBondedList) {
            addModel(device.getName(), device.getAddress());
        }

        requestPermission();
    }

    private void requestPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
            } else {
                startDiscovery();
            }
        } else {
            startDiscovery();
        }
    }

    private void startDiscovery() {
        BtManager.getInstance().startDiscovery();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mBtFoundReceiver);
    }

    class BtFoundReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device != null) {
                    //判断是否配对过
                    if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                        addModel(device.getName(), device.getAddress());
                    }
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {

            }
        }
    }

    /**
     * 添加数据
     *
     * @param name
     * @param address
     */
    private void addModel(String name, String address) {
        Log.e(TAG, "name:" + name + "address:" + address);

        if (TextUtils.isEmpty(name)) {
            return;
        }

        if (TextUtils.isEmpty(address)) {
            return;
        }

        boolean isAdd = false;
        for (int i = 0; i < mList.size(); i++) {
            if (mList.get(i).getAddress().equals(address)) {
                isAdd = true;
            }
        }

        if (isAdd) {
            return;
        }

        BtListModel model = new BtListModel();
        model.setName(name);
        model.setAddress(address);
        mList.add(model);
        mBtListAdapter.notifyDataSetChanged();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        for (int i = 0; i < permissions.length; i++) {
            if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                int result = grantResults[i];
                if (result == PackageManager.PERMISSION_GRANTED) {
                    startDiscovery();
                } else {
                    requestPermission();
                }
            }
        }
    }
}

再来看下封装的蓝牙管理类

package com.liuguilin.iot_bt.manager;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.content.Intent;

import java.io.IOException;
import java.util.Set;
import java.util.UUID;

/**
 * FileName: BtManager
 * Founder: LiuGuiLin
 * Create Date: 2019/2/12 11:39
 * Email: lgl@szokl.com.cn
 * Profile: 蓝牙控制管理类
 */
public class BtManager {

    private static BtManager mInstance = null;

    private BluetoothAdapter mBluetoothAdapter;

    private BtManager() {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    public static BtManager getInstance() {
        if (mInstance == null) {
            synchronized (BtManager.class) {
                if (mInstance == null) {
                    mInstance = new BtManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 是否支持
     *
     * @return
     */
    public boolean isSupport() {
        return mBluetoothAdapter != null;
    }

    /**
     * 是否打开
     *
     * @return
     */
    public boolean isEnabled() {
        return mBluetoothAdapter.isEnabled();
    }

    /**
     * 开关 无提示
     */
    public void open() {
        if (!isEnabled()) {
            mBluetoothAdapter.enable();
        }
    }

    /**
     * 开关 有提示
     *
     * @param mActivity
     * @param requestCode
     */
    public void open(Activity mActivity, int requestCode) {
        if (!isEnabled()) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            mActivity.startActivityForResult(intent, requestCode);
        }
    }

    /**
     * 获取已配对列表
     *
     * @return
     */
    public Set<BluetoothDevice> getBondedDevices() {
        return mBluetoothAdapter.getBondedDevices();
    }

    /**
     * 是否在搜索
     *
     * @return
     */
    public boolean isDiscovering() {
        return mBluetoothAdapter.isDiscovering();
    }

    /**
     * 开始搜索
     */
    public void startDiscovery() {
        if (!isDiscovering()) {
            mBluetoothAdapter.startDiscovery();
        }
    }

    /**
     * 停止搜索
     */
    public void cancelDiscovery() {
        if (isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
    }

    /**
     * 获取远程设备
     *
     * @param address
     * @return
     */
    public BluetoothDevice getRemoteDevice(String address) {
        return mBluetoothAdapter.getRemoteDevice(address);
    }

    /**
     * 获取客户端设备
     * @param name
     * @param uuid
     * @return
     */
    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) {
        try {
            return mBluetoothAdapter.listenUsingRfcommWithServiceRecord(name, uuid);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

好了,我们重点回到如何建立连接上,我们也知道,当我们点击设备的时候会跳转ChatActivity

                Intent intent = new Intent(MainActivity.this, ChatActivity.class);
                BtListModel model = mList.get(i);
                intent.putExtra("name", model.getName());
                intent.putExtra("address", model.getAddress());
                startActivity(intent);

那ChatActivity拿到这两个参数之后会做什么呢?

首先,会创建一个AcceptThread循环读取远程端发送过来的消息

private class AcceptThread extends Thread {
        // 服务端接口
        private BluetoothServerSocket serverSocket;
        // 获取到客户端的接口
        private BluetoothSocket socket;
        // 获取到输入流
        private InputStream is;

        public AcceptThread() {
            serverSocket = BtManager.getInstance().listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        }

        @Override
        public void run() {
            try {
                // 接收其客户端的接口
                socket = serverSocket.accept();
                // 获取到输入流
                is = socket.getInputStream();
                while (true) {
                    // 创建一个128字节的缓冲
                    byte[] buffer = new byte[128];
                    // 每次读取128字节,并保存其读取的角标
                    int count = is.read(buffer);
                    // 创建Message类,向handler发送数据
                    Message msg = new Message();
                    // 发送一个String的数据,让他向上转型为obj类型
                    msg.obj = new String(buffer, 0, count, "utf-8");
                    // 发送数据
                    mHandler.sendMessage(msg);
                }
            } catch (IOException e) {
                Log.e(MainActivity.TAG, e.toString());
            }
        }
    }

所以我们接收到数据,则是在这里获取到的,然后我们才申请建立连接

    /**
     * 建立连接
     *
     * @param address
     */
    private void connetSocket(String address) {
        if (selectDevice == null) {
            //通过地址获取到该设备
            selectDevice = BtManager.getInstance().getRemoteDevice(address);
        }

        try {
            if (clientSocket == null) {
                // 获取到客户端接口
                clientSocket = selectDevice.createRfcommSocketToServiceRecord(MY_UUID);
                // 向服务端发送连接
                clientSocket.connect();
                // 获取到输出流,向外写数据
                os = clientSocket.getOutputStream();
            }
            sendText("连接成功");
        } catch (IOException e) {
            Log.e(MainActivity.TAG, e.toString());
        }
    }

建立连接以后我们就可以发送数据了

    /**
     * 发送消息
     *
     * @param text
     */
    private void sendText(final String text) {
        if (!TextUtils.isEmpty(text)) {
            if (os != null) {
                // 以utf-8的格式发送出去
                try {
                    os.write(text.getBytes("UTF-8"));
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            addLeft(text);
                        }
                    });
                    Log.e(MainActivity.TAG, text);
                } catch (IOException e) {
                    Toast.makeText(this, "消息发送失败:" + e.toString(), Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(this, "设备未连接", Toast.LENGTH_SHORT).show();
            }
        }
    }

与此同时,对方的AcceptThread就会收到我们的消息然后达成互相通信的功能

如图

在这里插入图片描述

我们来看下ChatActivity的所有代码

package com.liuguilin.iot_bt;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.liuguilin.iot_bt.adapter.ChatListAdapter;
import com.liuguilin.iot_bt.manager.BtManager;
import com.liuguilin.iot_bt.model.ChatListModel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * FileName: ChatActivity
 * Founder: LiuGuiLin
 * Create Date: 2019/2/12 15:52
 * Email: lgl@szokl.com.cn
 * Profile:聊天
 */
public class ChatActivity extends AppCompatActivity implements View.OnClickListener {

    private RecyclerView mChatRyView;
    private EditText et_text;
    private Button btn_send;

    private ChatListAdapter mChatListAdapter;
    private List<ChatListModel> mList = new ArrayList<>();

    // UUID,蓝牙建立链接需要的
    private final UUID MY_UUID = UUID.fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");
    // 为其链接创建一个名称
    private final String NAME = "Bluetooth_Socket";
    // 选中发送数据的蓝牙设备,全局变量,否则连接在方法执行完就结束了
    private BluetoothDevice selectDevice;
    // 获取到选中设备的客户端串口,全局变量,否则连接在方法执行完就结束了
    private BluetoothSocket clientSocket;
    // 获取到向设备写的输出流,全局变量,否则连接在方法执行完就结束了
    private OutputStream os;
    // 服务端利用线程不断接受客户端信息
    private AcceptThread thread;

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            String text = (String) msg.obj;
            Log.e(MainActivity.TAG, "text:" + text);
            addRight(text);
            Toast.makeText(ChatActivity.this, text, Toast.LENGTH_SHORT).show();
            return false;
        }
    });

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

        initView();
    }

    private void initView() {

        mChatRyView = (RecyclerView) findViewById(R.id.mChatRyView);
        et_text = (EditText) findViewById(R.id.et_text);
        btn_send = (Button) findViewById(R.id.btn_send);

        btn_send.setOnClickListener(this);

        mChatRyView.setLayoutManager(new LinearLayoutManager(this));
        mChatListAdapter = new ChatListAdapter(this, mList);
        mChatRyView.setAdapter(mChatListAdapter);

        Intent intent = getIntent();
        String name = intent.getStringExtra("name");
        final String address = intent.getStringExtra("address");

        getSupportActionBar().setTitle(name);

        thread = new AcceptThread();
        thread.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                connetSocket(address);
            }
        }).start();

    }

    /**
     * 建立连接
     *
     * @param address
     */
    private void connetSocket(String address) {
        if (selectDevice == null) {
            //通过地址获取到该设备
            selectDevice = BtManager.getInstance().getRemoteDevice(address);
        }

        try {
            if (clientSocket == null) {
                // 获取到客户端接口
                clientSocket = selectDevice.createRfcommSocketToServiceRecord(MY_UUID);
                // 向服务端发送连接
                clientSocket.connect();
                // 获取到输出流,向外写数据
                os = clientSocket.getOutputStream();
            }
            sendText("连接成功");
        } catch (IOException e) {
            Log.e(MainActivity.TAG, e.toString());
        }
    }

    /**
     * 发送消息
     *
     * @param text
     */
    private void sendText(final String text) {
        if (!TextUtils.isEmpty(text)) {
            if (os != null) {
                // 以utf-8的格式发送出去
                try {
                    os.write(text.getBytes("UTF-8"));
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            addLeft(text);
                        }
                    });
                    Log.e(MainActivity.TAG, text);
                } catch (IOException e) {
                    Toast.makeText(this, "消息发送失败:" + e.toString(), Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(this, "设备未连接", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_send:
                String text = et_text.getText().toString().trim();
                sendText(text);
                et_text.setText("");
                break;
        }
    }

    private class AcceptThread extends Thread {
        // 服务端接口
        private BluetoothServerSocket serverSocket;
        // 获取到客户端的接口
        private BluetoothSocket socket;
        // 获取到输入流
        private InputStream is;

        public AcceptThread() {
            serverSocket = BtManager.getInstance().listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        }

        @Override
        public void run() {
            try {
                // 接收其客户端的接口
                socket = serverSocket.accept();
                // 获取到输入流
                is = socket.getInputStream();
                while (true) {
                    // 创建一个128字节的缓冲
                    byte[] buffer = new byte[128];
                    // 每次读取128字节,并保存其读取的角标
                    int count = is.read(buffer);
                    // 创建Message类,向handler发送数据
                    Message msg = new Message();
                    // 发送一个String的数据,让他向上转型为obj类型
                    msg.obj = new String(buffer, 0, count, "utf-8");
                    // 发送数据
                    mHandler.sendMessage(msg);
                }
            } catch (IOException e) {
                Log.e(MainActivity.TAG, e.toString());
            }
        }
    }

    private void addLeft(String text) {
        ChatListModel model = new ChatListModel();
        model.setType(ChatListAdapter.LEFT_TEXT);
        model.setLeftText(text);
        mList.add(model);
        mChatListAdapter.notifyDataSetChanged();
    }

    private void addRight(String text) {
        ChatListModel model = new ChatListModel();
        model.setType(ChatListAdapter.RIGHT_TEXT);
        model.setRightText(text);
        mList.add(model);
        mChatListAdapter.notifyDataSetChanged();
    }
}

这个只是一个简单的实现Demo,有兴趣的自己去钻研一下

点击下载

有兴趣的加入群:555974449

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐