此小项目主要讲述了蓝牙的开发基础
比如
打开蓝牙 关闭蓝牙 扫描附近设备 获取附近设备信息 连接附近设备 等等
1.清单文件 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wjn.buletoothdemo">
<!-- 允许程序链接网络 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 允许程序连接到已配对的蓝牙设备 -->
<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_COARSE_LOCATION"/>
<!-- 在Android5.0之前,是默认申请GPS硬件功能的。而在Android 5.0 之后,需要在manifest 中申明GPS硬件模块功能的使用 -->
<uses-feature android:name="android.hardware.location.gps"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2.布局文件 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/showtextview"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center|left"/>
<Button
android:id="@+id/open"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="打开蓝牙" />
<Button
android:id="@+id/scanner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="扫描附近蓝牙设备" />
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止扫描" />
<Button
android:id="@+id/close"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="关闭蓝牙" />
<Button
android:id="@+id/play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送数据" />
<Button
android:id="@+id/disconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="断开连接" />
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#FF0000"
android:dividerHeight="1dp">
</ListView>
</LinearLayout>
3.布局文件 item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center|left"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="设备名称:小米手环"
android:textSize="18sp" />
<TextView
android:id="@+id/adress"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center|left"
android:text="设备地址:192.168.4.115"
android:textSize="18sp" />
<TextView
android:id="@+id/classes"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center|left"
android:text="蓝牙类名:蓝牙类名"
android:textSize="18sp" />
<TextView
android:id="@+id/statees"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center|left"
android:text="蓝牙状态:蓝牙状态"
android:textSize="18sp" />
<TextView
android:id="@+id/typees"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center|left"
android:text="蓝牙类型:蓝牙类型"
android:textSize="18sp" />
<TextView
android:id="@+id/uuidses"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center|left"
android:text="蓝牙UUID:蓝牙UUID"
android:textSize="18sp" />
</LinearLayout>
4.BaseAdapter 显示附近设备列表
package com.wjn.buletoothdemo.adapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.wjn.buletoothdemo.R;
import java.util.List;
/**
* Created by Administrator on 2017/8/15.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List<BluetoothDevice> list;
public MyAdapter(Context context,List<BluetoothDevice> data) {
this.context=context;
this.list = data;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
public void setData(List<BluetoothDevice> data) {
this.list = data;
notifyDataSetChanged();
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if(view==null){
view=View.inflate(context, R.layout.item,null);
viewHolder=new ViewHolder();
viewHolder.nametextview= (TextView) view.findViewById(R.id.name);
viewHolder.addresstextview= (TextView) view.findViewById(R.id.adress);
viewHolder.classestextview= (TextView) view.findViewById(R.id.classes);
viewHolder.stateestextview= (TextView) view.findViewById(R.id.statees);
viewHolder.typeestextview= (TextView) view.findViewById(R.id.typees);
viewHolder.uuidsestextview= (TextView) view.findViewById(R.id.uuidses);
view.setTag(viewHolder);
}else{
viewHolder= (ViewHolder) view.getTag();
}
viewHolder.nametextview.setText("设备名称:"+list.get(i).getName());
viewHolder.addresstextview.setText("IP地址:"+list.get(i).getAddress());
viewHolder.classestextview.setText("蓝牙类名:"+list.get(i).getBluetoothClass());
viewHolder.stateestextview.setText("蓝牙状态:"+list.get(i).getBondState());
viewHolder.typeestextview.setText("蓝牙类型:"+list.get(i).getType());
viewHolder.uuidsestextview.setText("蓝牙UUID:"+list.get(i).getUuids());
return view;
}
public static class ViewHolder{
private TextView nametextview;
private TextView addresstextview;
private TextView classestextview;
private TextView stateestextview;
private TextView typeestextview;
private TextView uuidsestextview;
}
}
5.MainActivity 主页面
package com.wjn.buletoothdemo.activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.wjn.buletoothdemo.R;
import com.wjn.buletoothdemo.adapter.MyAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements View.OnClickListener,AdapterView.OnItemClickListener{
private TextView showtextview;
private Button open;
private Button scanner;
private Button stop;
private Button btn_disconnect;
private Button play;
private Button close;
private ListView listview;
private List<BluetoothDevice> mData = new ArrayList<>();
private MyAdapter ListAdapter;
private BluetoothManager manager;//蓝牙管理器
private BluetoothAdapter bluetoothAdapter;//蓝牙适配器
private boolean issupportbluetooth=false;//当前设备是否支持蓝牙
private boolean mScanning;//是否可扫描
private boolean isComment = false;//集合中是否已存在当前设备
private BluetoothGatt mBluetoothGatt;//链接扫描出来的设备
private List<BluetoothGattService> services = new ArrayList<>();
private List<BluetoothGattCharacteristic> characteristics = new ArrayList<>();
public static final UUID UUID1 = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb");
private static final UUID UUID2 = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb");
private static final UUID UUID3 = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
/**
* Handler机制
* */
private static final int FIND_SERVICE = 1;//蓝牙连接设备过程中 发现服务
private static final int SEND_DATA_FAIL = 2;//蓝牙连接设备过程中 发送数据失败
private static final int SEND_DATA_SUCCESS = 3;//蓝牙连接设备过程中 发送数据成功
private static final int SEND_DATA_NOT_PERMITTED = 33;//蓝牙连接设备过程中 没有发送数据权限
private static final int CONNECT_SUCCESS = 4;//蓝牙连接设备过程中 连接成功
private static final int CONNECT_FIAL = 5;//蓝牙连接设备过程中 连接失败或是断开连接
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int type=msg.what;
switch(type){
case FIND_SERVICE://蓝牙连接设备过程中 发现服务
for (int x = 0; x < services.size(); x++) {
characteristics = services.get(2).getCharacteristics();
break;
}
break;
case SEND_DATA_SUCCESS:
Toast.makeText(MainActivity.this,"数据发送成功!",Toast.LENGTH_SHORT).show();
break;
case SEND_DATA_FAIL:
Toast.makeText(MainActivity.this,"数据发送失败!",Toast.LENGTH_SHORT).show();
break;
case SEND_DATA_NOT_PERMITTED:
Toast.makeText(MainActivity.this,"无发送数据权限!",Toast.LENGTH_SHORT).show();
break;
case CONNECT_SUCCESS://蓝牙连接设备过程中 连接成功
Toast.makeText(MainActivity.this,"连接成功!",Toast.LENGTH_SHORT).show();
break;
case CONNECT_FIAL://蓝牙连接设备过程中 连接失败或是断开连接
Toast.makeText(MainActivity.this,"连接失败或是断开连接!",Toast.LENGTH_SHORT).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();//初始化view
}
/**
* 初始化view
* */
private void initView() {
showtextview= (TextView) findViewById(R.id.showtextview);
open = (Button) findViewById(R.id.open);
scanner = (Button) findViewById(R.id.scanner);
close = (Button) findViewById(R.id.close);
stop = (Button) findViewById(R.id.stop);
btn_disconnect = (Button) findViewById(R.id.disconnect);
play = (Button) findViewById(R.id.play);
play.setOnClickListener(this);
btn_disconnect.setOnClickListener(this);
open.setOnClickListener(this);
stop.setOnClickListener(this);
scanner.setOnClickListener(this);
close.setOnClickListener(this);
listview = (ListView) findViewById(R.id.listview);
listview.setOnItemClickListener(this);
//当前设备是否支持蓝牙
issupportbluetooth=getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
/**
* onResume方法
* */
@Override
protected void onResume() {
super.onResume();
initBluetooth();//初始化蓝牙
}
/**
* 初始化蓝牙
* */
private void initBluetooth() {
if (!issupportbluetooth) {
Toast.makeText(this, "当前设备不支持蓝牙!", Toast.LENGTH_SHORT).show();
}else{
//获取蓝牙管理器
manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
//获取蓝牙适配器
bluetoothAdapter = manager.getAdapter();
}
}
/**
* 各种点击事件的方法
* */
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.open://打开蓝牙
if(issupportbluetooth){
if (!bluetoothAdapter.isEnabled()) {
bluetoothAdapter.enable();
Toast.makeText(this, "正在打开蓝牙...", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "蓝牙已打开!", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(this, "当前设备不支持蓝牙!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.scanner://扫描附近设备
if(issupportbluetooth){
if (!bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "请先打开蓝牙!", Toast.LENGTH_SHORT).show();
return;
}
if (bluetoothAdapter != null && bluetoothAdapter.isDiscovering()) {
Toast.makeText(this, "正在扫描中,不要重复扫描!", Toast.LENGTH_SHORT).show();
return;
}
//开始扫描
scannerBluetooth();
Toast.makeText(this, "开始扫描蓝牙...", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "当前设备不支持蓝牙!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.close://关闭蓝牙
if(issupportbluetooth){
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
if (bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "蓝牙已关闭!", Toast.LENGTH_SHORT).show();
bluetoothAdapter.disable();
}
}else{
Toast.makeText(this, "当前设备不支持蓝牙!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.stop://停止扫描
if(issupportbluetooth){
if (bluetoothAdapter != null && bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.stopLeScan(mLeScanCallback);
Toast.makeText(this, "已停止扫描!", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(this, "当前设备不支持蓝牙!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.disconnect://断开连接
mBluetoothGatt.disconnect();
break;
case R.id.play://发送数据
sendData();
break;
}
}
/**
* 蓝牙扫描附近设备
* BluetoothDevice 几个方法
* String name=device.getName();
* String address=device.getAddress();
* int state=device.getBondState();
* int type=device.getType();
* ParcelUuid[] uuids=device.getUuids();
*/
private void scannerBluetooth() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, 10000);
mScanning = true;
//需要参数 BluetoothAdapter.LeScanCallback(返回的扫描结果)
bluetoothAdapter.startLeScan(mLeScanCallback);
}
/**
* 蓝牙扫面结果的回调接口
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
if(bluetoothDevice!=null&&bluetoothDevice.getName()!=null){
if(mData.size()==0){//集合中还没有任何设备
mData.add(bluetoothDevice);
runOnUiThread(new Runnable() {
@Override
public void run() {
//将扫描的到的bluetoothDevice添加到集合中
ListAdapter = new MyAdapter(MainActivity.this,mData);
listview.setAdapter(ListAdapter);
}
});
}else{//集合中已有设备
for(int x =0;x<mData.size();x++){
isComment=mData.get(x).getAddress().equals(bluetoothDevice.getAddress()) ? true : false;
if(isComment){//集合中已有此设备
isComment= false;
break;
}else{//集合中无此设备 添加
mData.add(bluetoothDevice);
ListAdapter.setData(mData);
}
}
}
}else{
Toast.makeText(MainActivity.this,"蓝牙扫描无结果!",Toast.LENGTH_SHORT).show();
}
}
};
/**
* Listview item点击事件 链接发现的设备
* */
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mScanning) {//正在扫描中
bluetoothAdapter.stopLeScan(mLeScanCallback);
mScanning = false;
}
//通过蓝牙设备地址 获取远程设备 开始连接
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(mData.get(position).getAddress());
//第二个参数 是否要自动连接
mBluetoothGatt = device.connectGatt(MainActivity.this, false, mBluetoothGattCallback);
}
/**
* 发送数据
* */
private void sendData() {
//1.准备数据
byte[] data = new byte[6];
data[0] = 0x55;
data[1] = (byte) 0xAA;
data[2] = 0x00;
data[3] = 0x03;
data[4] = 0x02;
data[5] = (byte) 0xFB;
if (bluetoothAdapter == null || mBluetoothGatt == null) {
Toast.makeText(MainActivity.this,"蓝牙异常,请重新连接!",Toast.LENGTH_SHORT).show();
}
//2.通过指定的UUID拿到设备中的服务也可使用在发现服务回调中保存的服务
BluetoothGattService bluetoothGattService = services.get(0);
//3.通过指定的UUID拿到设备中的服务中的characteristic,也可以使用在发现服务回调中通过遍历服务中信息保存的Characteristic
BluetoothGattCharacteristic gattCharacteristic = bluetoothGattService.getCharacteristic(UUID1);
//4.将byte数据设置到特征Characteristic中去
gattCharacteristic.setValue(data);
//5.将设置好的特征发送出去
mBluetoothGatt.writeCharacteristic(gattCharacteristic);
}
/**
* 蓝牙连接设备 或是发送数据到扫描的设备 回调接口
* */
private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
/**
* 连接成功后发现设备服务后调用此方法
* @param gatt
* @param status
*/
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {//发现该设备的服务
//拿到该服务 1,通过UUID拿到指定的服务 2,可以拿到该设备上所有服务的集合
List<BluetoothGattService> serviceList = mBluetoothGatt.getServices();
//可以遍历获得该设备上的服务集合,通过服务可以拿到该服务的UUID,和该服务里的所有属性Characteristic
for(int x=0;x<serviceList.size();x++){
services.add(serviceList.get(x));
}
Message message = Message.obtain();
message.what = FIND_SERVICE;
handler.sendMessage(message);
//可以通过service拿到服务的特性 BluetoothGattCharacteristic ---描述
}else{//未发现该设备的服务
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"未发现服务!",Toast.LENGTH_SHORT).show();
}
});
}
}
/**
* 蓝牙连接状态改变后调用 此回调 (断开,连接)
* @param gatt
* @param status
* @param newState
*/
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {//连接成功
Message message = Message.obtain();
message.what = CONNECT_SUCCESS;
handler.sendMessage(message);
//连接成功后去发现该连接的设备的服务
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//连接失败 或者连接断开都会调用此方法
Message message = Message.obtain();
message.what = CONNECT_FIAL;
handler.sendMessage(message);
}
}
/**
* Characteristic数据发送后调用此方法
* @param gatt
* @param characteristic
* @param status
*/
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {//写入成功
Message message = Message.obtain();
message.what = SEND_DATA_SUCCESS;
handler.sendMessage(message);
} else if (status == BluetoothGatt.GATT_FAILURE) {//写入失败
Message message = Message.obtain();
message.what = SEND_DATA_FAIL;
handler.sendMessage(message);
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {// 没有写入的权限
Message message = Message.obtain();
message.what = SEND_DATA_NOT_PERMITTED;
handler.sendMessage(message);
}
}
/**
* 某Characteristic的状态为可读时的回调
* @param gatt
* @param characteristic
* @param status
* */
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//读取到值,在这里读数据
if (status == BluetoothGatt.GATT_SUCCESS) {
readCharacterisricValue(characteristic);
}
}
/**
* 订阅了远端设备的Characteristic信息后
* 当远端设备的Characteristic信息发生改变后,回调此方法
* */
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
readCharacterisricValue(characteristic);
}
};
/**
* 读取BluetoothGattCharacteristic中的数据
* @param characteristic
*/
private void readCharacterisricValue(BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
StringBuffer buffer = new StringBuffer("0x");
int i;
for (byte b : data) {
i = b & 0xff;
buffer.append(Integer.toHexString(i));
}
showtextview.setText(buffer.toString());
}
/**
* onDestroy方法
* */
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothGatt = null;
}
}
}