ESP32S蓝牙09

ESP32S蓝牙09

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

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

 

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

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

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

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

 

这个程序是把之前的客户端和服务端的连个程序整合在一起,为了方便编程,我们使用了两个不同的视图界面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() {

   

    //连接状态,当APPBLE连接成功、或者连接断开时,都会触发这个事件

        @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 isEnableNotificationbluetoothGatt.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"/>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tongyue

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值