Android的更新速度太快,以至于目前搜到的很多蓝牙APP的源码都无法使用。花了三天时间,终于实现了蓝牙app的基本功能。

文章目录

  • 1.项目需求
  • 2.效果展示
  • 3.工程结构
  • 4.核心代码
    • 4.1蓝牙打开、关闭、搜索
    • 4.2蓝牙搜索,适配器显示
    • 4.3建立连接
    • 4.4创建线程,传输数据
  • 5.完整源码
  • 6.参考资料

1.项目需求

本项目需要制作一款APP,实现手机蓝牙和HC-05蓝牙模块连接,接收来自单片机的数据,并在APP页面上进行实时显示。

2.效果展示

这里只为测试APP的性能,因此将HC-05接TTL转USB模块,连接电脑,通过串口工具XCOM发送信息。
测试手机系统:Android 10
(由于gif太大传不上来,因此仅展示图片)

3.工程结构

4.核心代码

4.1蓝牙打开、关闭、搜索

/**
* 检测和开启蓝牙
*/
private void openBluetooth() {
   mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
   if (mBluetoothAdapter != null) {
       //判断蓝牙是否打开并可见
       if (!mBluetoothAdapter.isEnabled()) {
           //请求打开并可见
           Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
           startActivityForResult(intent,1);
           mToast.showToast("蓝牙已打开");
       }
       else{
           mToast.showToast("蓝牙已经打开");
       }
   }else{
       mToast.showToast("设备不支持蓝牙功能");
   }
}

/**
* 关闭蓝牙
*/
private void closeBluetooth() {
   mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
   if (mBluetoothAdapter != null) {
       //判断蓝牙是否打开并可见
       if (mBluetoothAdapter.isEnabled()) {
           //请求关闭
           mBluetoothAdapter.disable();
           mToast.showToast("蓝牙已关闭");
       }
       else{
           mToast.showToast("蓝牙已经关闭");
       }
   }else{
       mToast.showToast("设备不支持蓝牙功能");
   }
}

/**
* 搜索蓝牙设备
*/
private void discoverBluetooth(){
   if (mBluetoothAdapter.isDiscovering()) {
       mBluetoothAdapter.cancelDiscovery();
   }
   //搜索设备
   mBluetoothAdapter.startDiscovery();
   mToast.showToast("正在搜索设备");
}

4.2蓝牙搜索,适配器显示

MyArrayAdapter

public class MyArrayAdapter extends BaseAdapter {

    private final List<DeviceInformation> mDatas;
    private final Context mContext;

    public MyArrayAdapter(List<DeviceInformation> mDatas, Context mContext) {
        this.mDatas = mDatas;
        this.mContext = mContext;
    }

    @Override
    public int getCount() {
        return mDatas.size();
    }

    @Override
    public Object getItem(int i) {
        return mDatas.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        if (view == null) {
            view = LayoutInflater.from(mContext).inflate(R.layout.device_item_layout,null);
        }
        TextView nameTv = view.findViewById(R.id.device_name);
        TextView addressTv = view.findViewById(R.id.device_address);
        DeviceInformation deviceInformation = mDatas.get(i);
        nameTv.setText(deviceInformation.getDeviceName());
        addressTv.setText(deviceInformation.getDeviceAddress());
        return view;
    }
}

扫描显示

private void initReceiver() {
        //创建用于接收蓝牙状态改变广播的广播接收者
        mBLuetoothStateReceiver = new BroadcastReceiver(){
            @Override
            public void onReceive(Context context, Intent intent) {
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                switch (state){
                    case BluetoothAdapter.STATE_ON:
                        mToast.showToast("蓝牙已打开");
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        mToast.showToast("蓝牙已关闭");
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        mToast.showToast("蓝牙正在打开");
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        mToast.showToast("蓝牙正在关闭");
                        break;
                }
            }
        };
        //创建设备扫描广播接收者
        mBluetoothReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Log.d(TAG,"onReceive");

                String action = intent.getAction();
                if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    boolean isAdded = false;//标记扫描到的设备是否已经在数据列表里了
                    //获取扫描到的设备
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    //保存设备的信息
                    DeviceInformation deviceInformation = new DeviceInformation(device.getName(),device.getAddress());
                    for (DeviceInformation data : mDatas) {
                        //判断已保存的设备信息里是否有一样的
                        if (data.getDeviceAddress().equals(deviceInformation.getDeviceAddress())) {
                            isAdded = true;
                            break;
                        }
                    }
                    if (!isAdded) {
                        //通知UI更新
                        mDatas.add(deviceInformation);
                        mAdapter.notifyDataSetChanged();
                    }
                }
            }
        };
        //注册广播接收者
        IntentFilter filter1 = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        IntentFilter filter2 = new IntentFilter(BluetoothDevice.ACTION_FOUND);

        registerReceiver(mBLuetoothStateReceiver,filter1);
        registerReceiver(mBluetoothReceiver,filter2);
    }
    //权限是否授予,给出提示
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSION_REQUEST_CONSTANT: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    mToast.showToast("权限授权成功");
                }else{
                    mToast.showToast("权限授权失败");
                }
                return;
            }
        }
    }

4.3建立连接

    /**
     * 与目标设备建立连接
     */
    private void connectDevice() {

        //获取默认蓝牙设配器
        mBlueToothAdapter = BluetoothAdapter.getDefaultAdapter();
        //通过地址拿到该蓝牙设备device
        mDevice = mBlueToothAdapter.getRemoteDevice(mAddress);
        try {
            //建立socket通信
            mBluetoothSocket = mDevice.createRfcommSocketToServiceRecord(mUUID);
            mBluetoothSocket.connect();
            if (mBluetoothSocket.isConnected()) {
                mToast.showToast("连接成功");
                //开启接收数据的线程
                ReceiveDataThread thread = new ReceiveDataThread();
                thread.start();
            }else{
                mToast.showToast("连接失败,结束重进");
            }
        } catch (IOException e) {
            e.printStackTrace();
            mToast.showToast("连接出错! ");
            finish();
            try {
                mBluetoothSocket.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            if (mBluetoothSocket.isConnected()) {
                //关闭socket
                mBluetoothSocket.close();
                mBlueToothAdapter = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4.4创建线程,传输数据

 /**
   * 负责接收数据的线程
   */
  public class ReceiveDataThread extends Thread{

      private InputStream inputStream;

      public ReceiveDataThread() {
          super();
          try {
              //获取连接socket的输入流
              inputStream = mBluetoothSocket.getInputStream();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      @Override
      public void run() {
          super.run();
          while (true){
              try {
                  //每次运行需新生成一个
                  byte[] buffer = new byte[16];
                  inputStream.read(buffer);
                  String a = new String(buffer,0,buffer.length);
                  runOnUiThread(new Runnable() {
                      @Override
                      public void run() {
                          //将收到的数据显示在TextView上
                          mReceiveContent.append(a);
                          //System.out.println(a);
                          /**
                           * 注:inputStream控制固定字节读入时并不稳定,读入数据容易出现漏读情况,因此采用其它方法来更新数据
                           * 在每次UI线程中,获取收到数据的最后一行,即最新数据,截取出来进行显示
                           * 由于append会自动换行,因此line_num-2
                           */
                          Layout layout = mReceiveContent.getLayout();
                          String text = mReceiveContent.getText().toString();
                          int line_num = layout.getLineCount();
                          int start = layout.getLineStart(line_num - 2);
                          int end = layout.getLineEnd(line_num - 2);
                          String line = text.substring(start, end);
                          //实时将数据显示更新,这里后期根据单片机数据进行修改
                          mid.setText(line);
                          mtemp.setText(line);
                      }
                  });
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
  }

5.完整源码

下载链接

6.参考资料

Android 蓝牙串口通信Demo
Android蓝牙开发学习

更多推荐

Android:能接收HC05数据的APP