酒已经准备好了,各位看客,请准备好小板凳。
理论性的东西就不写太多了,推荐一个挺好的博文
http://www.jianshu.com/p/de82f411a7fc
官方文档的翻译(简书):
http://www.jianshu.com/p/bc408af3dd92
复制一些重要的理论过来,也方便你们查看:
二、解释:
BLE分为Service、Characteristic、Descriptor三部分,每个部分都拥有不同的 UUID来标识
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
android:name="android.hardware.bluetooth_le" android:required="true"/>
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >
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 ListBlueToothDevice_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(); } } }); } } }; }
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 ListlistDatas; //listview需要用到的信息包括蓝牙名字和蓝牙设备的mac地址; private LayoutInflater layoutInflater; ListViewAdapter(Context mcontext, List 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"?>xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > android:id="@+id/device_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="蓝牙设备名字" android:layout_margin="2dp" /> android:id="@+id/device_address" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="蓝牙设备的名字" android:layout_margin="5dp" />
一个实体类,主要是蓝牙设备的name和address;
package com.ityingli.www.bluetoothtest2; /** * Created by Administrator on 2017/7/29. */ public class BlueTooth_item__Bean { //该数据用于listview的item 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