Android蓝牙ble扫描

来源:互联网 发布:gis空间数据库 编辑:程序博客网 时间:2024/04/29 04:08

酒已经准备好了,各位看客,请准备好小板凳。


理论性的东西就不写太多了,推荐一个挺好的博文

http://www.jianshu.com/p/de82f411a7fc

 官方文档的翻译(简书):

http://www.jianshu.com/p/bc408af3dd92

复制一些重要的理论过来,也方便你们查看:


扫盲

  1. 蓝牙有传统蓝牙(3.0以下)和低功耗蓝牙(ble,又称蓝牙4.0)之分
  2. android手机必须系统版本4.3及以上才支持BLE API。低功耗蓝牙较传统蓝牙, 传输速度更快,覆盖范围更广,安全性更高,延迟更短,耗电极低等等优点,这也是为什么近年来智能穿戴的东西越来越多,越来越火
  3. 传统蓝牙与低功耗蓝牙通信方式也有所不同,传统的一般通过socket方式,而低功耗蓝牙是通过Gatt协议来实现
  4. 低功耗蓝牙也叫BLE

二、解释:

  1. BLE分为ServiceCharacteristicDescriptor三部分,每个部分都拥有不同的 UUID来标识

2一个BLE设备可以拥有多个Service,一个Service可以包含多个Characteristic, 一个Characteristic包含一个Value和多个Descriptor,一个Descriptor包含一个Value

3通信数据一般存储在Characteristic内,目前一个Characteristic中存储的数据最大为20 byte

4与Characteristic相关的权限字段主要有READ、WRITE、WRITE_NO_RESPONSE、NOTIFY。 Characteristic具有的权限属性可以有一个或者多个

非常感谢作者对知识的分享


这篇只会介绍Android蓝牙4.0Ble的扫描,大大们,请。。。。。



效果图先奉上,直接拍个照




酒喝完了,上菜
在6.0版本前,使用蓝牙功能,只需要配置下面的权限即可:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

6.0以后

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

添加动态权限

下面是ActivityMain的布局
<?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"    >    <Button        android:id="@+id/startScale"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="startScale" />    <Button        android:id="@+id/stopScale"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="stopScale" />    <ProgressBar        android:id="@+id/progressbar"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="stopScale"/>    <ListView        android:id="@+id/listview"        android:layout_width="match_parent"        android:layout_height="match_parent"></ListView></LinearLayout>


MainActivity的代码
package com.ityingli.www.bluetoothtest2;import android.Manifest;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothManager;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.location.LocationManager;import android.os.Build;import android.os.Bundle;import android.os.Handler;import android.provider.Settings;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.Toast;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private static final int REQUEST_CODE_LOCATION_SETTINGS = 2; //用于Gps打开    private BluetoothManager bluetoothManager;        //蓝牙管理器    private BluetoothAdapter bluetoothAdapter;       //蓝牙适配器    private Boolean scaleIng ;                       //是否正在扫描    private static  final int REQUEST_ENABLE_BLE = 1;    //蓝牙请求    private static final long SCALE_PERIOD= 10*1000;     //扫描时长   10    //布局中的控件    private Button startScaleButton,stopScaleButton;       //开始扫描按钮,停止扫描按钮    private ListView listview;                             //展示扫描到的结果Listview;    private List<BlueTooth_item__Bean>   BlueToothDevice_Info;          //蓝牙设备的信息    private ListViewAdapter adapter;    private ProgressBar progressbar;    private Handler handler;    private static final int REQUEST_CODE_ACCESS_COARSE_LOCATION = 1;   //动态申请权限    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();                                                  //初始化控件        initEvent();                                                //初始化事件        initData();                                                  //初始化数据        adapter = new ListViewAdapter(MainActivity.this,BlueToothDevice_Info);       //listView的适配器        listview.setAdapter(adapter);        //检测当前设备是否支持蓝牙ble        if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){            Toast.makeText(MainActivity.this, "当前蓝牙不支持蓝牙设备", Toast.LENGTH_SHORT).show();            finish();        }        //通过蓝牙管理器得到一个蓝牙适配器        bluetoothManager  = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);        bluetoothAdapter = bluetoothManager.getAdapter();        if(bluetoothAdapter==null){            Toast.makeText(MainActivity.this,"该设备不支持蓝牙",Toast.LENGTH_SHORT).show();        }        //android6.0之后要求有定位权限才可以扫描到蓝牙        //动态申请权限        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){   //如果api版本大于23Android6.0的时候            //判断是否具有权限            if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)!=PackageManager.PERMISSION_GRANTED){                //判断是否需要向用户解释为什么需要申请权限                if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_COARSE_LOCATION)){                    Toast.makeText(MainActivity.this,"需要获取定位才可以使用BLE扫描",Toast.LENGTH_SHORT).show();                }                //请求权限                ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_CODE_ACCESS_COARSE_LOCATION);//请求码            }        }    }    // //执行完上面的请求权限后,系统会弹出提示框让用户选择是否允许改权限。选择的结果可以在回到接口中得知:    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        if(requestCode==REQUEST_CODE_ACCESS_COARSE_LOCATION){            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0 PERMISSION_DENIED = -1                //permission was granted, yay! Do the contacts-related task you need to do.                //这里进行授权被允许的处理            } else {                //permission denied, boo! Disable the functionality that depends on this permission.                //这里进行权限被拒绝的处理            }        } else {            super.onRequestPermissionsResult(requestCode, permissions, grantResults);        }    }    //以上就是如何在应用运行的过程中申请位置权限。做完上面你以为就 OK 了,但是可能你会发现,并没有什么卵用,依然不能搜索到周围的 Ble 设备,原因是可能的你的位置服务(定位 GPS)没有打开。    //检测定位是否打开    public static final boolean isLocationEnable(Context context) {        LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);        boolean networkProvider = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);        boolean gpsProvider = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);        if (networkProvider || gpsProvider) return true;        return false;    }    //如果没有就打开,进入定位设置界面,让用户自己选择是否打开定位。选择的结果获取:    private void setLocationService() {        Intent locationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);        this.startActivityForResult(locationIntent, REQUEST_CODE_LOCATION_SETTINGS);    }    @Override    protected void onResume() {        super.onResume();        //确保蓝牙可以使用,如果不可以使用一个弹窗        if(!bluetoothAdapter.enable()){            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);            startActivityForResult(intent,REQUEST_ENABLE_BLE);        }    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        //不同打开蓝牙        if(requestCode ==REQUEST_ENABLE_BLE && resultCode == RESULT_CANCELED){            finish();            return;        }        //定位        if (requestCode == REQUEST_CODE_LOCATION_SETTINGS) {            if (isLocationEnable(this)) {                //定位已打开的处理            } else {                //定位依然没有打开的处理                Toast.makeText(MainActivity.this,"请打开GPS",Toast.LENGTH_SHORT).show();            }        } else super.onActivityResult(requestCode, resultCode, data);        super.onActivityResult(requestCode, resultCode, data);    }    private void initData() {        //用于定时取消扫描        handler = new Handler();        //模拟蓝牙设备的信息        BlueToothDevice_Info = new ArrayList<>();        for(int i  = 0  ;i<10;i++){            BlueTooth_item__Bean bluetooth_device_item_info = new BlueTooth_item__Bean();            bluetooth_device_item_info.blueToothDevie_Name = "蓝牙设备名字"+i;            bluetooth_device_item_info.blueToothDevie_Adress = "蓝牙设备mac地址"+i;            BlueToothDevice_Info.add(bluetooth_device_item_info);        }    }    private void initEvent() {        startScaleButton.setOnClickListener(this);                    //开始扫描事件        stopScaleButton.setOnClickListener(this);                    //停止扫描    }    private void initView() {        startScaleButton = (Button)findViewById(R.id.startScale);               //初始化控件。开始扫描按钮        stopScaleButton= (Button)findViewById(R.id.stopScale);                  //初始化控件,停止扫描按钮         listview = (ListView) findViewById(R.id.listview);                    //用于展示扫描到的设备信息,Listview        progressbar = (ProgressBar) findViewById(R.id.progressbar);        progressbar.setVisibility(View.GONE);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.startScale:                                             //扫描操作     (扫描一定的事件就自动关闭扫描)            BlueToothDevice_Info.clear();            progressbar.setVisibility(View.VISIBLE);            bluetoothAdapter.startLeScan(scaleCallback);            handler.postDelayed(new Runnable() {                @Override                public void run() {                    bluetoothAdapter.stopLeScan(scaleCallback);                    progressbar.setVisibility(View.GONE);                }            },10000);            break;        case R.id.stopScale:                                                  //停止扫描            progressbar.setVisibility(View.GONE);            bluetoothAdapter.stopLeScan(scaleCallback);            break;        }    }    //扫描到设备之后的回调方法    private BluetoothAdapter.LeScanCallback  scaleCallback = new BluetoothAdapter.LeScanCallback() {        @Override        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {            if(device.getName()!=null && device.getAddress()!=null) {                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        final BlueTooth_item__Bean bean = new BlueTooth_item__Bean();                        bean.blueToothDevie_Name = device.getName();                        bean.blueToothDevie_Adress = device.getAddress();                        if(BlueToothDevice_Info.contains(bean)) {                            //如果集合中已经包含相同的对象,则不添加进去                        }else{                            BlueToothDevice_Info.add(bean);                            adapter.notifyDataSetChanged();                        }                    }                });            }        }    };}


Listview的适配器

package com.ityingli.www.bluetoothtest2;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.List;/** * Created by Administrator on 2017/7/29. */public class ListViewAdapter extends BaseAdapter {    private Context mcontext;                                    //上下文    private List<BlueTooth_item__Bean>  listDatas;              //listview需要用到的信息包括蓝牙名字和蓝牙设备的mac地址;    private LayoutInflater layoutInflater;    ListViewAdapter(Context mcontext, List<BlueTooth_item__Bean> listDatas){        this.mcontext = mcontext;        this.listDatas = listDatas;        layoutInflater =  LayoutInflater.from(mcontext);    }    @Override    public int getCount() {        return listDatas.size();    }    @Override    public Object getItem(int position) {        return listDatas.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View view, ViewGroup parent) {        ListviewHolder listviewHolder;        if(view ==null){            listviewHolder = new ListviewHolder();            view = layoutInflater.inflate(R.layout.layout_listview_item,null);            listviewHolder.device_Name = (TextView) view.findViewById(R.id.device_name);            listviewHolder.device_Address= (TextView) view.findViewById(R.id.device_address);            view.setTag(listviewHolder);        }else{            listviewHolder =  (ListviewHolder) view.getTag();        }        listviewHolder.device_Name.setText(listDatas.get(position).blueToothDevie_Name);        listviewHolder.device_Address.setText(listDatas.get(position).blueToothDevie_Adress);        return view;    }    static class  ListviewHolder{        private TextView device_Name;        private TextView device_Address;    }}


适配器中用的的布局(ListView的Item的布局)

<?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/device_name"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="蓝牙设备名字"        android:layout_margin="2dp"        />    <TextView        android:id="@+id/device_address"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="蓝牙设备的名字"        android:layout_margin="5dp"        /></LinearLayout>


一个实体类,主要是蓝牙设备的name和address;

package com.ityingli.www.bluetoothtest2;/** * Created by Administrator on 2017/7/29. */public class BlueTooth_item__Bean {           //该数据用于listviewitem       String blueToothDevie_Name;       String blueToothDevie_Adress;          //蓝牙设备的名字和mas地址    //判断内容是否相等    @Override    public boolean equals(Object obj) {        if(obj instanceof  BlueTooth_item__Bean){             if(blueToothDevie_Adress.equals(((BlueTooth_item__Bean) obj).blueToothDevie_Adress) && blueToothDevie_Name.equals(((BlueTooth_item__Bean) obj).blueToothDevie_Name)){                 return true;             }        }else{            return false;        }        return super.equals(obj);    }    @Override    public int hashCode() {        return blueToothDevie_Adress.hashCode();    }}


注释也是挺详细的了,各位晚安,不早了    2017/7/30    0:53

原创粉丝点击