ESP32S蓝牙09

经过了半个多月的学习,我们从米思齐的积木编程到Arduino IDE的模块编程、从APPInventor在线积木APP制作到Eclipse的纯代码编程、从传统蓝牙到BLE低功耗蓝牙、从蓝牙客户端到蓝牙服务端,由浅入深,足迹遍布蓝牙开发编程的方方面面。如今已经可以迎来了收官之作,我们准备做一个实用性的BLE蓝牙测试APP。

我们准备把BLE蓝牙的客户端和服务端两个程序整合在一起,本来以为这个是很简单的整合,不就1+1吗,但是在实际的操作过程中,还是状况频发,搞得我晕头转向的,经过了反反复复的摸索,不断地踩坑,加上一点瞎猫碰上死耗子的狗屎运,最后跌跌撞撞地完成了这个测试APP。

esp32选型 esp32s_ide

 

先上实用效果图吧。这个测试APP其实就是把之前的BLE客户端和服务端的两个程序整合在一起而已,使用如图所示,两部手机安装的其实是同一个整合完的程序。打开APP,在手机屏幕的顶端会有两个按钮“扫描蓝牙”和“蓝牙服务”,用户可以在客户端和服务器端。

首先选择一部(右边),点击“蓝牙服务”按钮,即进入了蓝牙服务端的页面,点击“开启服务”,会显示开启服务成功。这样,右边的这部手机运行的就是蓝牙服务端的程序,就会不断地向外广播服务消息,告诉周围的BLE蓝牙:我是服务器,我已经准备好了,可以随时连接我。

其次选择另外一部(左边),点击“扫描蓝牙”按钮,即开始了蓝牙客户端的程序,同时把“蓝牙服务”的按钮设置为不可用(程序在使用之前,必须选择只能运行客户端或服务器端的一种模式)。经过扫描就能扫到右边那部手机的蓝牙服务信号了,选择列表就可以连接,然后进行收发信息的通讯了。

经过这样的整合,用户使用起来很方便,只要下载安装同一个测试APP,就可以进行BLE蓝牙设备的测试了。这个是我们亲手制作的,比起网上下载的一些蓝牙测试,已经先进多了,可以说已经把网上的哪些测试APP甩出了好几条大街了,这难道不香吗。

esp32选型 esp32s_ide_02

 

这个程序是把之前的客户端和服务端的连个程序整合在一起,为了方便编程,我们使用了两个不同的视图界面Activity。首先我们新建一个安卓工程,(再说一下,我使用的是Eclipse编程环境,安装的是Android API24版本,具体的安装过程可以看我的另外一篇博客《升级Android SDK API24笔记》),工程名字BlueToothLE,默认设置就行。其次,我们要给这个程序添加第二个视图界面(窗口),我们在Eclipse左边的窗口中,选择工程下src文件夹右击鼠标,然后选择“new—Other—Android—Android Activity”菜单,会打开一个新建视图的对话框,输入新建窗口的名字为activity_server,其他的选择默认就可以,这样Eclipse就会为我们新建了一个视图窗口,我们会在src文件夹、res\layout文件夹、配置文件AndroidManifest. Xml中都能看到相应的新建窗口的消息了。

然后我们就是把之前的客户端和服务端的界面设计、程序代码分别复制到这两个视图中,在配置文件AndroidManifest. Xml中申请安卓版本号和蓝牙等权限后,就差不多了。当然有些地方的代码还是需要整合修改的。

踩坑过程记录:

然而程序的调试并不顺利,状况频发,这最大的坑是显示收发消息的标签控件显示不正常,基本上处于罢工的状态,偶尔能显示部分内容,算是你烧高香、他心情好的结局了。这种状况绝对不行,用户肯定不会答应,必须彻底解决:

1. 首先我猜想,会不会是消息在蓝牙设备的传输过程中,因为某种原因搁置了,消息没有传送过去。根据之前我做WiFi测试时的经验,我在每发送一条消息的后面,加上了回车换行符(”\n\r”),这样是为了让蓝牙设备立刻马上发送消息。测试结果没有改善,还是显示不顺。

2. 我查阅了网上别人的代码,有人说需要用Handler来接收消息,更新标签的内容。这个是因为我们从蓝牙接收消息时,用的是蓝牙的服务“线程+回调”,也就是在蓝牙的侦听线程里面接收到了别人发送过来的消息的。这样在非主线程中,是无法直接更改界面控件内容的(界面中的输入文本获取、标签文本输出、按钮侦听等,都是APP的主线程控制的),这里面涉及到了线程的内容,也就是安卓在线程控制中是这样规定的,非主线程(这里的蓝牙侦听)是不能直接控制和改变主线程的内容(这里的界面,标签文本的显示内容),而是需要使用Handler + Message来实现。结果更改了代码,测试还是没有结果。

3. 究竟是传输过程出了问题,还是显示过程出了问题呢。在测试的过程中(我们在两部手机中都安装了这个测试APP,分别运行客户端和服务端,两部手机进行收发消息的测试),结果一次非常偶然的操作,我按了一下手机底部中间的Home软按钮,手机显示了桌面,而测试程序退出到后台运行;当我点击手机底部的三条横线的软按钮,把这个测试程序从后台调到前台运行时,奇迹般的出现了,之前没有显示的收发消息,一下子全部都显示出来了。这个偶然的发现,基本上肯定了消息的收发传输过程是没问题的,问题应该出现在显示的部分了。 等之前的消息全部显示出来后,再次让测试程序继续收发操作,显示还是不顺利,还是要等到程序从后台转到前台的时候,才能整批的显示。

4. 然后还是经过了反反复复的尝试、修改代码、测试效果依然无果,显示问题一直不正常,整个屏幕好像睡着了似的,非得我们给他触碰一下,他才动一动。 

还是一个偶然的机会,在测试收发的过程中,我不经意地点击了一下发送按钮前面的文本输入框,结果之前没有显示的消息一下子都显示了!这样也能显示!!而且接着尝试收发的消息,都能及时地显示出来!!!   我就点击了一下输入文本框,整个程序就真的好像从昏睡中醒来一样,生龙活虎,显示哗哗哗的,一点问题都没有。

那我们把原来显示收发消息的标签TextView更换成输入文本EditText,这样不就可以了吗?(当然,改为输入文本框的话,默认会获得活动的插入光标的,也就是在这个文本框中,会有一个一闪一闪的活动输入光标,这样影响使用效果,也影响使用心情,还是决定把这个光标关掉msgstr02.setFocusable(false);),这样这个输入文本框显示起来就和标签一样的效果了。 

这个就是两天的测试过程中最大的发现,收获最大的成果。把显示收发消息的标签控件,替换为隐藏光标的输入文本框,在程序的运行中,显示不及时的问题彻彻底底地解决了。我们在两部手机中互相收发消息,都等立马显示出来了,这个测试APP也算是功德圆满,可以发布使用了。

回顾一下这次的踩坑过程,真的拼着一口不肯放松的劲,在加上踩狗屎的运气。当然奇迹真滴是给有准备的人,这才真正明白,所谓的灵感,都是经过了无数次的试错的结果。

最后贴上程序源码,算是笔记吧。这个是主程序  MainActivity. java

package com.example.bluetoothle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@SuppressLint("NewApi")
public class MainActivity extends AppCompatActivity {
//public final static UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  //通用蓝牙的标志
    private UUID mServiceUUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
    private UUID mReadUUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
    private UUID mWriteUUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
   
    private Button scan_button, send_button, discon_button, server_button;
    private TextView msgstr;
    private EditText editstr, msgstr02;
    private BluetoothAdapter bleadapter;
    private BluetoothGatt bluetoothGatt;
    private BluetoothGattService bluetoothGattServices;
    private BluetoothGattCharacteristic character_read, character_write;
    private BluetoothDevice bledevice;
    private List<BluetoothDevice> mDeviceList;
    private ArrayList list;
    private ArrayAdapter adapter;
    private ListView mListView;
    private boolean connected = false;
   
    private Handler handler = new Handler() {
@Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
msgstr02.setText(msgstr02.getText().toString() + "in: " + (String) msg.obj);
    }
    };
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     
msgstr = (TextView) findViewById(R.id.txt_msg);
msgstr02 = (EditText) findViewById(R.id.txt_msg02);
editstr = (EditText) findViewById(R.id.txt_edit01);
scan_button = (Button) findViewById(R.id.btn_client);
send_button = (Button) findViewById(R.id.btn_send);
discon_button = (Button) findViewById(R.id.btn_discon);
server_button = (Button) findViewById(R.id.btn_server);
mListView = (ListView) findViewById(R.id.listView1);
       
msgstr02.setFocusable(false);
editstr.setFocusable(false);
editstr.setOnTouchListener(new View.OnTouchListener() {
@Override
            public boolean onTouch(View v, MotionEvent event) {
editstr.setFocusable(true);
editstr.setFocusableInTouchMode(true);
editstr.requestFocus();
                return false;
            }
        });
//搜索按钮
scan_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
//初始化ble设配器
server_button.setEnabled(false);
             BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bleadapter = manager.getAdapter();
//判断蓝牙是否开启,若是关闭则请求打开蓝牙
             if (bleadapter == null || !bleadapter.isEnabled()) {
                 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                 startActivityForResult(intent, 1);
             }
            
msgstr.setText("扫描中,请稍候 ...");
//初始化列表变量,一个用于存储自定义类,一个用于存储字符串
mDeviceList=new ArrayList<BluetoothDevice>();
list = new ArrayList();
//把扫描过程放进一个线程里面进行
             new Thread(new Runnable() {
@Override
                    public void run() {
//如果发现一个BLE设备,就会执行一次callback回调函数
bleadapter.startLeScan(callback);
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
e.printStackTrace();
                        }
bleadapter.stopLeScan(callback);
msgstr.setText("");
                    }
                }).start();               
        }
        });
       
//发送消息按钮
send_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
editstr.setFocusable(false);
             if(connected == true) {
                 String str = editstr.getText().toString() + "\n\r";
character_write.setValue(str);
bluetoothGatt.writeCharacteristic(character_write);
msgstr02.setText(msgstr02.getText().toString() + "ou: " + str);
             }
        }
        });
       
//断开连接按钮
discon_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
//断开连接
             if(connected == true) {
bluetoothGatt.disconnect();
connected = false;
             }
        }
        });
       
//跳转到蓝牙服务页面按钮
server_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
             Intent intent=new Intent();
intent.setClass(MainActivity.this, ServerActivity.class);
                MainActivity.this.startActivity(intent);
        }
        });
       
//列表框侦听,当用户点击选择蓝牙时,连接蓝牙
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//获取用户选中的蓝牙设备
bledevice = mDeviceList.get(position);
msgstr.setText("connect: " + bledevice.getAddress());
//连接设备的方法,返回值为bluetoothgatt类型
//根据手机的版本,版本较高 或较低的时候
//回调函数gattcallback,管理蓝牙的连接、获取服务、读写消息
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
bluetoothGatt = bledevice.connectGatt(MainActivity.this, false, gattcallback, BluetoothDevice.TRANSPORT_LE);
            else
bluetoothGatt = bledevice.connectGatt(MainActivity.this, false, gattcallback);
            }
          });
    }
   
//这个是蓝牙扫描的回调函数,每当扫描到一个BLE设备时,就会运行一次这个函数
    public BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() {
@Override
        public void onLeScan(final BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
        
         if (bluetoothDevice != null){
//这里给大家另外两种显示扫描结果的方法,可以用消息框或标签来显示
//Toast.makeText(MainActivity.this, bluetoothDevice.getName(), Toast.LENGTH_SHORT).show();
//showresult.append(bleDevice.getName() + "   " + bleDevice.getMac() + "\n");
                if(!mDeviceList.contains(bluetoothDevice)) {
mDeviceList.add(bluetoothDevice); 
//list是存储字符串的集合,adapter是连接字符串到列表框的工具
                  list.add(bluetoothDevice.getName() + "   " + bluetoothDevice.getAddress());
adapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_list_item_1, list);
mListView.setAdapter(adapter);
                }
         }
        }
    };
   
//这个是蓝牙管理的回调函数,管理BLE的连接、获取服务、读写消息
    private BluetoothGattCallback gattcallback = new BluetoothGattCallback() {
   
//连接状态,当APP与BLE连接成功、或者连接断开时,都会触发这个事件
@Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            runOnUiThread(new Runnable() {
@Override
                public void run() {  
                    switch (newState) {
//已经连接
                        case BluetoothProfile.STATE_CONNECTED:
msgstr.setText("已连接" + "\n");
//当连接成功,就获取BLE的服务,并触发获取服务的事件
bluetoothGatt.discoverServices();
                            break;
//正在连接
                        case BluetoothProfile.STATE_CONNECTING:
msgstr.setText("正在连接" + "\n");
                            break;
//连接断开
                        case BluetoothProfile.STATE_DISCONNECTED:
msgstr.setText("已断开" + "\n");
bluetoothGatt.close();
msgstr02.setText("");
                            break;
//正在断开
                        case BluetoothProfile.STATE_DISCONNECTING:
msgstr.setText("断开中" + "\n");
                            break;
                    }
                }
            });
        }
//这个是获取BLE服务的事件,如果获取成功
@Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
           
            if (status == BluetoothGatt.GATT_SUCCESS) {
              BluetoothGattService gattService = bluetoothGatt.getService(mServiceUUID);
//获取指定的UUID服务不为空时
                if(gattService != null){
//获取指定的UUID读写通道
bluetoothGattServices = gattService;
character_read = gattService.getCharacteristic(mReadUUID);
character_write = gattService.getCharacteristic(mWriteUUID);
//把读取通道设置为可侦听、可读取状态
                  if (character_read != null)
                       setCharacteristicNotification(character_read, true);
connected = true;
msgstr.setText("已连接,获取服务成功");
                }else{
msgstr.setText("获取服务失败");
                }
            }
           
        }
@Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
           
        }
@Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }
       
//这个是侦听事件,当有数据从BLE设备传入APP的时候,就会引发这个事件
@Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            if(characteristic == character_read) {
              Message mesg = new Message();
mesg.what = 1;
mesg.obj = new String(characteristic.getValue()) ;
                MainActivity.this.handler.sendMessage(mesg);
            }
           
        }
@Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
        }
@Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
        }
@Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }
@Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }
@Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }
    } ;
   
//这个是把某个通道设置为可侦听状态
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        boolean isEnableNotification =  bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        if(isEnableNotification) {
//一个读写通道里面,可能一次就传递多个类型的数值,每个类型数字都要设置侦听属性
            List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
            if(descriptorList != null && descriptorList.size() > 0) {
                for(BluetoothGattDescriptor descriptor : descriptorList) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
                }
            }
        }
    }
   
}
  
   
这个是界面设计   activity_main. xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FF00FFFF"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
  
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
      
       <Button
android:id="@+id/btn_client"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FF0080FF"
           android:text="蓝牙扫描" />
      
       <Button
android:id="@+id/btn_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFFFF00"
           android:text="蓝牙服务" />
      
</LinearLayout>
  
<TextView
android:id="@+id/txt_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ListView
android:id="@+id/listView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible" />
  
<Button
android:id="@+id/btn_discon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
      android:text="断开蓝牙" />
   <EditText
android:id="@+id/txt_edit01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
               android:text="hello server" />
  
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
      android:text="发送消息" />
   <EditText
android:id="@+id/txt_msg02"
android:minLines="12"
android:gravity="top"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这个是第二个视图程序  ServerActivity. java
package com.example.bluetoothle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.content.Intent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.UUID;
@SuppressLint("NewApi")
//public class ServerActivity extends ActionBarActivity {
public class ServerActivity extends AppCompatActivity {
//public final static UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  //通用蓝牙的标志
    private UUID mServiceUUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
    private UUID mReadUUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
    private UUID mWriteUUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
   
    private Button scan_button, send_button, discon_button;
    private TextView msgstr;
    private EditText editstr, msgstr02;
    private BluetoothAdapter bleadapter;
    private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
    private BluetoothGatt bluetoothGatt;
    private BluetoothGattServer gattServer;
    private BluetoothGattService bluetoothGattServices;
    private BluetoothDevice bluetoothDevice;
    private BluetoothGattCharacteristic character_read, character_write;
    private boolean connected = false;
    private Handler handler = new Handler() {
@Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
msgstr02.setText(msgstr02.getText().toString() + "in: " + (String) msg.obj);
    }
    };
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_server);
       
       
msgstr = (TextView) findViewById(R.id.txt_msg);
msgstr02 = (EditText) findViewById(R.id.txt_msg02);
editstr = (EditText) findViewById(R.id.txt_edit01);
scan_button = (Button) findViewById(R.id.btn_scan);
send_button = (Button) findViewById(R.id.btn_send);
discon_button = (Button) findViewById(R.id.btn_discon);
       
msgstr02.setFocusable(false);
editstr.setFocusable(false);
editstr.setOnTouchListener(new View.OnTouchListener() {
@Override
            public boolean onTouch(View v, MotionEvent event) {
editstr.setFocusable(true);
editstr.setFocusableInTouchMode(true);
editstr.requestFocus();
                return false;
            }
        });
       
//初始化ble设配器
        BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bleadapter = manager.getAdapter();
//判断蓝牙是否开启,若是关闭则请求打开蓝牙
        if (bleadapter == null || !bleadapter.isEnabled()) {
//方式一:请求打开蓝牙
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, 1);
        }
// 获取蓝牙ble广播对象
        assert bleadapter != null;
mBluetoothLeAdvertiser = bleadapter.getBluetoothLeAdvertiser();
gattServer = manager.openGattServer(this, bluetoothGattServerCallback);
       
//BLE服务的初始化配置,很重要
//配置的顺序是 服务service  特征(通道)Characteristic  属性Descriptor
//配置服务名、 主服务类型
        BluetoothGattService service=new BluetoothGattService(mServiceUUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
//配置一个读的特征 (可读、可写、可侦听)
character_read=new BluetoothGattCharacteristic(mReadUUID, 
             BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 
             BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
        BluetoothGattDescriptor descriptor=new BluetoothGattDescriptor(mReadUUID, 
             BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
character_read.addDescriptor(descriptor);
service.addCharacteristic(character_read);
//配置一个写的特征  (可写)
character_write=new BluetoothGattCharacteristic(mWriteUUID, BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_WRITE);
service.addCharacteristic(character_write);
//添加到
gattServer.addService(service);
       
       
//开启广播按钮
scan_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
//开启广播需要传入三个参数: 基本设置   附加数据   回调函数
             AdvertiseSettings settings = new AdvertiseSettings.Builder()
                        .setConnectable(true) //是否被连接
                        .setTimeout(0)        //超时时间
                        .build();
        
//广播数据设置
                AdvertiseData advertiseData = new AdvertiseData.Builder()
                        .setIncludeDeviceName(true)    //是否在广播中携带设备的名称
                        .setIncludeTxPowerLevel(true)  //是否在广播中携带信号强度
                        .build();
//扫描回应的广播设置
                AdvertiseData scanResponseData = new AdvertiseData.Builder()
                        .setIncludeTxPowerLevel(true)  //是否在广播中携带设备的名称
                        .addServiceData(new ParcelUuid(mServiceUUID), new byte[]{1,2}) //在scanrecord中添加的数据
                        .build();
        
//设置BLE设备的名称
bleadapter.setName("BLEServer");
//开启广播
mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponseData, mAdvertiseCallback);            
        }
        });
       
//发送消息按钮
send_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
editstr.setFocusable(false);
             if(connected == true) {
                 String str = editstr.getText().toString() + "\n\r";
character_write.setValue(str);
gattServer.notifyCharacteristicChanged(bluetoothDevice, character_write, false);
msgstr02.setText(msgstr02.getText().toString() + "ou: " + str);
             }
                         
        }
        });
       
//停止广播按钮
discon_button.setOnClickListener(new View.OnClickListener() {
@Override
            public void onClick(View v) {
             if (connected == true) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
connected = false;
             }
        }
        });
     
    }
   
    private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            super.onStartSuccess(settingsInEffect);
msgstr.setText("开启广播成功" + "\n");
        }
@Override
        public void onStartFailure(int errorCode) {
            super.onStartFailure(errorCode);
msgstr.setText("开启广播失败" + "\n");
        }
    };
   
//广播服务管理状态回调
    private BluetoothGattServerCallback bluetoothGattServerCallback=new BluetoothGattServerCallback() {
       
//连接状态的回调
@Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            super.onConnectionStateChange(device, status, newState);
            if(newState == BluetoothProfile.STATE_CONNECTED) {
bluetoothDevice = device;
msgstr.setText("已连接到  " + device.getName() + "\n");
connected = true;
            }
            if(newState == BluetoothProfile.STATE_DISCONNECTED) {
msgstr.setText("连接已断开 " + "\n");
msgstr02.setText("");
            } 
        }
       
@Override
        public void onServiceAdded(int status,BluetoothGattService service){
            super.onServiceAdded(status,service);
msgstr.setText("添加服务成功");
        }
//获取接收,接收的数据为参数中的value
@Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
                Message mesg = new Message();
mesg.what = 1;
mesg.obj = new String(value);
                ServerActivity.this.handler.sendMessage(mesg);
        }
@Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
            super.onDescriptorReadRequest(device, requestId, offset, descriptor);
        }
       
@Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
           
        }
@Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
        }
    };
   
}
  
   
这个是第二个视图界面设计   activity_server. xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFF00"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
  
<Button
android:id="@+id/btn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
      android:text="开始广播" />
  
<TextView
android:id="@+id/txt_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
  
<Button
android:id="@+id/btn_discon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
      android:text="停止广播" />
  
   <EditText
android:id="@+id/txt_edit01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
               android:text="hello client" />
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
      android:text="发送消息" />
   <EditText
android:id="@+id/txt_msg02"
android:minLines="12"
android:gravity="top"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这个是安卓的版本号以及权限申请    在AndroidManifest. xml
<uses-sdk
android:minSdkVersion="11"
        android:targetSdkVersion="21" />
    
<!-- 添加蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.location.gps" />
<!-- Android6.0 蓝牙扫描才须要-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>