Android蓝牙开发知识总结(搜索,配对,连接,数据互传)

         最近公司开发需要用到蓝牙,花了大约一天的时间总结整理了一下。主要是为了以后自己方便看。有需要的朋友可以看下。欢迎一起讨论。后面会带上demo。里面是实现了蓝牙搜索,配对,连接,数据互传。

首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。

<uses-permission android:name="android.permission.BLUETOOTH" />允许程序连接到已配对的蓝牙设备。 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />允许程序发现和配对蓝牙设备。
Android 6.0后需要加上 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

一、我们需要用到了几个相关的类:

    1.BluetoothAdapter 顾名思义,蓝牙适配器,直到我们建立bluetoothSocket连接之前,都要不断操作它

      BluetoothAdapter里的方法很多,常用的有以下几个:

      cancelDiscovery() 根据字面意思,是取消发现,也就是说当我们正在搜索设备的时候调用这个方法将不再继续搜索

      disable()关闭蓝牙

      enable()打开蓝牙,这个方法打开蓝牙不会弹出提示,更多的时候我们需要问下用户是否打开,以下这两行代码同样是打开蓝牙,不过会提示用户:

Intemtenabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enabler,reCode);//同startActivity(enabler);

      getAddress()获取本地蓝牙地址

      getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter

      getName()获取本地蓝牙名称

      getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备

      getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)

      isDiscovering()判断当前是否正在查找设备,是返回true

      isEnabled()判断蓝牙是否打开,已打开返回true,否则,返回false

     listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步

      startDiscovery()开始搜索,这是搜索的第一步

    2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备

      createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket


这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket
这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter

   3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不过了,既然是Socket,方法就应该都差不多,这个类一种只有三个方法

两个重载的accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!


还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接

      close()这个就不用说了吧,关闭资源!

    4.BluetoothSocket,跟BluetoothServerSocket相对,是客户端

      一共5个方法,不出意外,都会用

      close(),关闭

      connect()连接

      getInptuStream()获取输入流

      getOutputStream()获取输出流

      getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

二、直接上代码。

MainActivity主要开启蓝牙,需要动态获取权限的获取权限。蓝牙传递数据分客户端和服务端,这里给一个入口,进客户端和服务端。
package com.qtwl.bluetooth;

import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener {
    BluetoothAdapter mBluetoothAdapter;
    public static final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button service = findViewById(R.id.service);
        Button clent = findViewById(R.id.clent);

        service.setOnClickListener(this);
        clent.setOnClickListener(this);

        if (Build.VERSION.SDK_INT >= 6.0) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1000);
        }

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter != null) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                // 设置蓝牙可见性,最多300秒
                intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                startActivity(intent);
            }
        } else {
            Toast.makeText(this, "没有蓝牙模块", Toast.LENGTH_SHORT).show();
        }

    }

    @Override
    public void onClick(View view) {
        if (mBluetoothAdapter == null) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                // 设置蓝牙可见性,最多300秒
                intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                startActivity(intent);
                return;
            }
        }

        Intent intent = null;


//        要在两台设备上的应用之间创建连接,必须同时实现服务器端和客户端机制,因为其中一台设备必须开放服务器套接字,而另一台设备必须发起连接(使用服务器设备的 MAC 地址发起连接)。
//        服务器设备和客户端设备分别以不同的方法获得需要的 BluetoothSocket。服务器将在传入连接被接受时收到套接字。 客户端将在其打开到服务器的 RFCOMM 通道时收到该套接字。
        switch (view.getId()) {
            case R.id.service://服务端
                intent = new Intent(this, ServiceActivity.class);
                break;

            case R.id.clent://客户端
                intent = new Intent(this, ClentActivity.class);
                break;

        }
        startActivity(intent);
    }
}

  

ServiceActivity服务端的代码。
package com.qtwl.bluetooth;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

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

/**
 * 连接为服务器
 * 当您需要连接两台设备时,其中一台设备必须通过保持开放的 BluetoothServerSocket 来充当服务器。
 * 1.通过调用 listenUsingRfcommWithServiceRecord(String, UUID) 获取 BluetoothServerSocket。
 * 当客户端尝试连接此设备时,它会携带能够唯一标识其想要连接的服务的 UUID。 两个 UUID 必须匹配,在下一步中,连接才会被接受。
 * 通过调用 accept() 开始侦听连接请求。
 * 除非您想要接受更多连接,否则请调用 close()。
 * 这将释放服务器套接字及其所有资源,但不会关闭 accept() 所返回的已连接的 BluetoothSocket。 与 TCP/IP 不同,RFCOMM 一次只允许每个通道有一个已连接的客户端,因此大多数情况下,在接受已连接的套接字后立即在 BluetoothServerSocket 上调用 close() 是行得通的。
 */


public class ServiceActivity extends BaseActivity {

    BluetoothServerSocket mmServerSocket;
    TextView title;
    TextView tvReceive;

    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            tvReceive.setText(tvReceive.getText() + "\n" + msg.obj.toString());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service);
        title = findViewById(R.id.tv_service_isStart);
        tvReceive = findViewById(R.id.tv_service);


        try {
            mmServerSocket = BluetoothAdapter.getDefaultAdapter().listenUsingRfcommWithServiceRecord("com.qtwl.bluetooth", UUID.fromString(MainActivity.MY_UUID));
            title.setText("蓝牙服务器开启成功");
        } catch (IOException e) {
            e.printStackTrace();
            title.setText("蓝牙服务器开启失败");
        }

        //写一个线程来做服务器,等待客户端来连接它
        new Thread() {
            @Override
            public void run() {
                super.run();
                while (true) {
                    try {
                        showToast("等待别人来连接");
                        BluetoothSocket socket = mmServerSocket.accept();
                        if (socket != null) {
                            Message msg = Message.obtain();
                            InputStream is = socket.getInputStream();
                            byte[] buffer = new byte[1024 * 128];
                            int count = is.read(buffer);

                            msg.obj = new String(buffer, 0, count, "utf-8");
                            handler.sendMessage(msg);

                            OutputStream os = socket.getOutputStream();
                            os.write("服务器收到了".getBytes("UTF-8"));
                            showToast("服务器做了返回");

                        }
                    } catch (IOException e) {
                        break;
                    }
                }
            }
        }.start();
    }
}

客户端的代码,主要是搜索附近的蓝牙,并配对。

package com.qtwl.bluetooth;

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.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 作为客户端
 */
public class ClentActivity extends BaseActivity {
    ListView mLvBle;//蓝牙列表
    Button search;//搜索附近蓝牙

    private List<BluetoothDevice> mDeviceList = new ArrayList<>();//蓝牙列表
    private BleAdapter mAdapter;

    BluetoothAdapter mBluetoothAdapter;

    /**
     * 广播接收者
     */
    private BroadcastReceiver bTReceive = new 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);
                Log.i("BroadcastReceiver", "BluetoothDevice.ACTION_FOUND  device = "+device.getName()+"  "+device.getAddress());
                for (BluetoothDevice d : mDeviceList) {
                    if (TextUtils.isEmpty(d.getAddress()) || TextUtils.isEmpty(device.getAddress()) || d.getAddress().equalsIgnoreCase(device.getAddress())) {
                        return;
                    }
                }
                mDeviceList.add(device);
                mAdapter.notifyDataSetChanged();
            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                Log.e("BroadcastReceiver", "BluetoothDevice.ACTION_BOND_STATE_CHANGED");
                BluetoothDevice blnDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                setProgressMsg("正在配对..");
                switch (blnDevice.getBondState()) {
                    case BluetoothDevice.BOND_BONDING://正在配对
                        setProgressMsg("正在配对..");
                        break;

                    case BluetoothDevice.BOND_BONDED://配对结束
                        mAdapter.notifyDataSetChanged();
                        setProgressMsg("配对完成,开始连接..");
                        startActivity(blnDevice);
                        break;

                    case BluetoothDevice.BOND_NONE://取消配对/未配对
                        showToast("配对失败!");
                        for (BluetoothDevice d : mDeviceList) {
                            if (d.getName().equalsIgnoreCase(blnDevice.getName())) {
                                mAdapter.notifyDataSetChanged();
                                return;
                            }
                        }
                        dismissProgressDialog();
                    default:
                        break;
                }
            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                Log.e("BroadcastReceiver", "BluetoothAdapter.ACTION_BOND_STATE_CHANGED");
                Toast.makeText(context, "BluetoothAdapter.ACTION_BOND_STATE_CHANGED", Toast.LENGTH_SHORT).show();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_clent);
        mLvBle = findViewById(R.id.lv_ble);//蓝牙列表
        search = findViewById(R.id.btn_search);//搜索附近蓝牙

         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        registerBTReceiver();// 注册蓝牙相关的各种广播

        //给蓝牙列表设置数据
        mAdapter = new BleAdapter(this);
        mAdapter.setData(mDeviceList);
        mLvBle.setAdapter(mAdapter);
        mLvBle.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                if (mBluetoothAdapter != null)
                    mBluetoothAdapter.cancelDiscovery();
                BluetoothDevice device = mDeviceList.get(i);
                bondBT(device);
            }
        });

        search.setOnClickListener(new View.OnClickListener() {//点击搜索按钮
            @Override
            public void onClick(View view) {
                if (mBluetoothAdapter.isDiscovering()) {
                    return;
                }
                mBluetoothAdapter.startDiscovery();
            }
        });
    }

    /**
     * 注册广播
     */
    public void registerBTReceiver() {
        // 设置广播信息过滤
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);   //发现蓝牙事件
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//蓝牙扫描结束事件
//        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//状态改变
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        // 注册广播接收器,接收并处理搜索结果
        registerReceiver(bTReceive, intentFilter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        mBluetoothAdapter.cancelDiscovery();
    }

    @Override
    protected void onResume() {
        super.onResume();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注销广播
        unregisterReceiver(bTReceive);
    }

    /**
     * 绑定蓝牙
     */
    private void bondBT(BluetoothDevice device) {
        showProgressDialog("配对蓝牙开始");
        // 搜索蓝牙设备的过程占用资源比较多,一旦找到需要连接的设备后需要及时关闭搜索
        // 获取蓝牙设备的连接状态
        int connectState = device.getBondState();
        switch (connectState) {
            case BluetoothDevice.BOND_NONE: // 未配对
                setProgressMsg("开始配对");
                try {
                    Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
                    createBondMethod.invoke(device);
                } catch (Exception e) {
                    showToast("配对失败!");
                    dismissProgressDialog();
                    e.printStackTrace();
                }
                break;

            case BluetoothDevice.BOND_BONDED: // 已配对
                //进入连接界面
                startActivity(device);
                break;
        }
    }

    //如果配对好了,就跳转到发消息界面
    private void startActivity(BluetoothDevice device) {
        dismissProgressDialog();
        Intent intent = new Intent(this, ClentSendActivity.class);
        intent.putExtra("device", device);

        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(intent);
    }





}
ClentSendActivity客户端连接的代码。客户端连接服务器,并发送数据,然后收到服务器返回的数据。
package com.qtwl.bluetooth;

import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

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

/**
 * 客户端发消息
 */
public class ClentSendActivity extends BaseActivity {
    private BluetoothDevice device;
    private BluetoothSocket bTSocket;
    TextView btNmae;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_clent_send);
        btNmae = findViewById(R.id.bluetooth_name);
        device = getIntent().getParcelableExtra("device");
        btNmae.setText(device.getName() + "  " + device.getAddress());
        connectDevice();

    }

    /**
     * 连接蓝牙
     */
    private void connectDevice() {
        showProgressDialog("正在连接蓝牙");
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    bTSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(MainActivity.MY_UUID)); //创建一个Socket连接:只需要服务器在注册时的UUID号
                    bTSocket.connect(); //连接
                    showToast("连接成功");
                    dismissProgressDialog();

                    OutputStream os = bTSocket.getOutputStream();
                    os.write("test 测试数据".getBytes("UTF-8"));
                    showToast("写数据成功");

                    InputStream is = bTSocket.getInputStream();
                    byte[] buffer = new byte[1024 * 128];
                    int count = is.read(buffer);
                    String string = new String(buffer, 0, count, "utf-8");
                    showToast("读到数据 string = " + string);

                } catch (IOException e) {
                    showToast("连接异常,请退出本界面再次尝试!");
                    dismissProgressDialog();
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

主要功能上面都有,这个是上面的 源码

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值