移动开发第六次作业——蓝牙通信的简要设计与开发

一、蓝牙通信基础知识、原理

为了让支持蓝牙的设备能够在彼此之间传输数据,它们必须先通过配对过程形成通信通道。其中一台设备(可检测到的设备)需将自身设置为可接收传入的连接请求。另一台设备会使用服务发现过程找到此可检测到的设备。在可检测到的设备接受配对请求后,这两台设备会完成绑定过程,并在此期间交换安全密钥。二者会缓存这些密钥,以供日后使用。完成配对和绑定过程后,两台设备会交换信息。当会话完成时,发起配对请求的设备会发布已将其链接到可检测设备的通道。但是,这两台设备仍保持绑定状态,因此在未来的会话期间,只要二者在彼此的范围内且均未移除绑定,便可自动重新连接。
蓝牙通信和socket通信原理基本上是一致的,下面我给大家上一张图(图为Socket通信图)。左为客户端Socket连接的一个流程:首先获取一个客户端Socket(),然后连接上,就可以读取数据和发送数据了。右为服务端Socket操作流程:
在这里插入图片描述

二、蓝牙通信的过程步骤

在这里插入图片描述
蓝牙通信的具体步骤如下:
1,首先开启蓝牙
2,搜索设备
3,创建蓝牙socket,获取输入输出流
4,读取和写入数据
5,断开连接关闭蓝牙

三、重要的类和接口

1.BluetoothAdapter

代表本地蓝牙适配器(蓝牙无线电)。BluetoothAdapter是所有蓝牙交互的入口。使用这个你可以发现其他蓝牙设备,查询已配对的设备列表,使用一个已知的MAC地址来实例化一个BluetoothDevice,以及创建一个BluetoothServerSocket来为监听与其他设备的通信。

2.BluetoothDevice

代表一个远程蓝牙设备,使用这个来请求一个与远程设备的BluetoothSocket连接,或者查询关于设备名称、地址、类和连接状态等设备信息。

3.BluetoothSocket

代表一个蓝牙socket的接口(和TCP Socket类似)。这是一个连接点,它允许一个应用与其他蓝牙设备通过InputStream和OutputStream交换数据。

4.BluetoothServerSocket

代表一个开放的服务器socket,它监听接受的请求(与TCP ServerSocket类似)。为了连接两台Android设备,一个设备必须使用这个类开启一个服务器socket。当一个远程蓝牙设备开始一个和该设备的连接请求,BluetoothServerSocket将会返回一个已连接的BluetoothSocket,接受该连接。

5.BluetoothClass

描述一个蓝牙设备的基本特性和性能。这是一个只读的属性集合,它定义了设备的主要和次要的设备类以及它的服务。但是,它没有描述所有的蓝牙配置和设备支持的服务,它只是暗示了设备的类型。

6.BluetoothProfile

一个表示蓝牙配置文件的接口。一个Bluetooth profile是一个基于蓝牙的通信无线接口定义。一个例子是Hands-Free profile。更多的讨论请见Working with Profiles。

7.BluetoothHeadset

提供对移动手机使用的蓝牙耳机的支持。它包含了Headset and Hands-Free (v1.5)配置文件。

8.BluetoothA2dp

定义高品质的音频如何通过蓝牙连接从一个设备传输到另一个设备。”A2DP“是Advanced Audio Distribution Profile的缩写。

9.BluetoothHealth

表示一个Health Device Profile代理,它控制蓝牙服务。

10.BluetoothHealthCallback

一个抽象类,你可以使用它来实现BluetoothHealth的回调函数。你必须扩展这个类并实现回调函数方法来接收应用程序的注册状态改变以及蓝牙串口状态的更新。

11.BluetoothHealthAppConfiguration

表示一个应用程序配置,Bluetooth Health第三方应用程序注册和一个远程Bluetooth Health设备通信。

12BluetoothProfile.ServiceListener

一个接口,当BluetoothProfile IPC客户端从服务器上建立连接或断开连接时,它负责通知它们(也就是,运行在特性配置的内部服务)

四、核心代码:

1、服务器
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;

public AcceptThread() {
    // Use a temporary object that is later assigned to mmServerSocket
    // because mmServerSocket is final.
    BluetoothServerSocket tmp = null;
    try {
        // MY_UUID is the app's UUID string, also used by the client code.
        tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
    } catch (IOException e) {
        Log.e(TAG, "Socket's listen() method failed", e);
    }
    mmServerSocket = tmp;
}

public void run() {
    BluetoothSocket socket = null;
    // Keep listening until exception occurs or a socket is returned.
    while (true) {
        try {
            socket = mmServerSocket.accept();
        } catch (IOException e) {
            Log.e(TAG, "Socket's accept() method failed", e);
            break;
        }

        if (socket != null) {
            // A connection was accepted. Perform work associated with
            // the connection in a separate thread.
            manageMyConnectedSocket(socket);
            mmServerSocket.close();
            break;
        }
    }
}

// Closes the connect socket and causes the thread to finish.
public void cancel() {
    try {
        mmServerSocket.close();
    } catch (IOException e) {
        Log.e(TAG, "Could not close the connect socket", e);
    }
}

}

2、客户端
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;

public ConnectThread(BluetoothDevice device) {
    // Use a temporary object that is later assigned to mmSocket
    // because mmSocket is final.
    BluetoothSocket tmp = null;
    mmDevice = device;

    try {
        // Get a BluetoothSocket to connect with the given BluetoothDevice.
        // MY_UUID is the app's UUID string, also used in the server code.
        tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
    } catch (IOException e) {
        Log.e(TAG, "Socket's create() method failed", e);
    }
    mmSocket = tmp;
}

public void run() {
    // Cancel discovery because it otherwise slows down the connection.
    bluetoothAdapter.cancelDiscovery();

    try {
        // Connect to the remote device through the socket. This call blocks
        // until it succeeds or throws an exception.
        mmSocket.connect();
    } catch (IOException connectException) {
        // Unable to connect; close the socket and return.
        try {
            mmSocket.close();
        } catch (IOException closeException) {
            Log.e(TAG, "Could not close the client socket", closeException);
        }
        return;
    }

    // The connection attempt succeeded. Perform work associated with
    // the connection in a separate thread.
    manageMyConnectedSocket(mmSocket);
}

// Closes the client socket and causes the thread to finish.
public void cancel() {
    try {
        mmSocket.close();
    } catch (IOException e) {
        Log.e(TAG, "Could not close the client socket", e);
    }
}

}

共同通讯处理:
package com.example.blue.connect;

import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

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

public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private final Handler mHandler;

public ConnectedThread(BluetoothSocket socket, Handler handler) {
    mmSocket = socket;
    InputStream tmpIn = null;
    OutputStream tmpOut = null;
    mHandler = handler;

    try {
        tmpIn = socket.getInputStream();
        tmpOut = socket.getOutputStream();
    } catch (IOException e) { }

    mmInStream = tmpIn;
    mmOutStream = tmpOut;
}


public void run() {
    byte[] buffer = new byte[1024]; 
    int bytes;


    //一直读数据
    while (true) {
        try {

            bytes = mmInStream.read(buffer);

            if( bytes >0) {
                Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA, new String(buffer, 0, bytes, "utf-8"));
                mHandler.sendMessage(message);
            }
            Log.d("GOTMSG", "message size" + bytes);
        } catch (IOException e) {
            mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));
            break;
        }
    }
}

//发送数据
public void write(byte[] bytes) {
    try {
        mmOutStream.write(bytes);
    } catch (IOException e) { }
}

public void cancel() {
    try {
        mmSocket.close();
    } catch (IOException e) { }
}

}
constant常量类代码
public class Constant {
public static final String CONNECTTION_UUID = “00001101-0000-1000-8000-00805F9B34FB”;
/**
* 开始监听
/
public static final int MSG_START_LISTENING = 1;
/
*
* 结束监听
/
public static final int MSG_FINISH_LISTENING = 2;
/
*
* 有客户端连接
/
public static final int MSG_GOT_A_CLINET = 3;
/
*
* 连接到服务器
/
public static final int MSG_CONNECTED_TO_SERVER = 4;
/
*
* 获取到数据
/
public static final int MSG_GOT_DATA = 5;
/
*
* 出错
*/
public static final int MSG_ERROR = -1;
}

DeviceAdaptor
public class DeviceAdapter extends BaseAdapter {
private Context mContext;
private List mDate;

public DeviceAdapter(List<BluetoothDevice> Date, Context context){
    mDate = Date;
    mContext = context;

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

@Override
public Object getItem(int position) {
    return mDate.get(position);
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder = null;
    if(convertView==null){
        viewHolder = new ViewHolder();
        convertView = LayoutInflater.from(mContext).inflate(R.layout.deviceadapter_layout,null);
        viewHolder.textView1 = (TextView)convertView.findViewById(R.id.textview1);
        viewHolder.textView2 = (TextView)convertView.findViewById(R.id.textview2);
        convertView.setTag(viewHolder);
    }else{
        viewHolder = (ViewHolder) convertView.getTag();
    }
    //获取蓝牙设备
    BluetoothDevice bluetoothDevice = (BluetoothDevice) getItem(position);
    viewHolder.textView1.setText("Name="+bluetoothDevice.getName());
    viewHolder.textView2.setText("Address"+bluetoothDevice.getAddress());
    return convertView;
}

public class ViewHolder{
    public TextView textView1;
    public  TextView textView2;

}
public void refresh(List<BluetoothDevice> data){
    mDate = data;
    notifyDataSetChanged();
}

}

BLUETOOTHCONTROLLER
public class BlueToothController {
private BluetoothAdapter mAapter;

public BlueToothController() {
    mAapter = BluetoothAdapter.getDefaultAdapter();
}

public BluetoothAdapter getAdapter() {
    return mAapter;
}
/**
 * 打开蓝牙
 * @param activity
 * @param requestCode
 */
public void turnOnBlueTooth(Activity activity, int requestCode) {
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    activity.startActivityForResult(intent, requestCode);

// mAdapter.enable();
}

/**
 * 打开蓝牙可见性
 * @param context
 */
public void enableVisibly(Context context) {
    Intent discoverableIntent = new
            Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
    context.startActivity(discoverableIntent);
}

/**
 * 查找设备
 */
public void findDevice() {
    assert (mAapter != null);
    mAapter.startDiscovery();
}

/**
 * 获取绑定设备
 * @return
 */
public List<BluetoothDevice> getBondedDeviceList() {
    return new ArrayList<>(mAapter.getBondedDevices());
}

}

五、结果截图

在这里插入图片描述
在这里插入图片描述

六、GITEE源码

童志成的代码仓库

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值