蓝牙4.0 的一些实现
一. 蓝牙介绍
1. 蓝牙介绍
- 1.蓝牙:BlueTooth 短距离数据传输
- 2.应用:蓝牙耳机 蓝牙音箱
- 3.功能:
- 打开蓝牙:
- 关闭蓝牙:
- 扫描附近的蓝牙设备:
- 配对蓝牙:
- 传输文件:
- 4.打开蓝牙:隐式意图打开蓝牙
- 5.关闭蓝牙:adapter.disable()
- 6.扫描:adapter.startDiscovery()
- 7.配对:device.createBond()
- 8.获取配对的设备:adapter.getBonedDevices()
- 9.传输数据
- (1)客户端
- (2)服务端:
- 一般保证蓝牙设备打开状态,不要点击关闭
简单的界面展示(左:为Demo中实现, 右为当前简单的实现界面):
2. 部分代码实现
- 在清单文件中添加权限 4个权限
<uses-permission android:name="android.permission.BLUETOOTH"/> <!-- 蓝牙连接配对权限-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <!-- 扫描蓝牙设备权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- GPS定位权限-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- 通过WIFI或基站获取位置权限-->
- MainActivity 中进行动态授权, 由于蓝牙会默认添加, 所以只进行位置定位的动态的授权, 代码如下:
// 数组用于存放权限
String[] strings = new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 判断系统版本是否是6.0以上
// 判断权限是否授权
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(strings, 101); //动态获取权限
}
}
- 初始化View控件 及点击事件, 这里用的是实现接口监听(OnClickListener),
布局文件在最下面: 见activity_main布局
private void initView() {
bt_open = (Button) findViewById(R.id.bt_open);
bt_open.setOnClickListener(this);
bt_scan = (Button) findViewById(R.id.bt_scan);
bt_scan.setOnClickListener(this);
bt_close = (Button) findViewById(R.id.bt_close);
bt_close.setOnClickListener(this);
bt_show = (Button) findViewById(R.id.bt_show);
bt_show.setOnClickListener(this);
lv_show = (ListView) findViewById(R.id.lv2);
lv_showAll = (ListView) findViewById(R.id.lv);
//显示已匹配的蓝牙设备
show_myAdapter = new MyAdapter(this, list_show);
lv_show.setAdapter(show_myAdapter);
//显示所有可匹配的蓝牙设备
showAll_myAdapter = new MyAdapter(this, list_showAll);
lv_showAll.setAdapter(showAll_myAdapter);
// 可匹配设备中 点击蓝牙设备 发起配对请求
lv_showAll.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
list_showAll.get(position).createBond(); //发送配对请求
}
});
// 已匹配设备中 点击蓝牙设备 发送信息, 或进行其他操作, 这里只发送信息
lv_show.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final BluetoothDevice device = list_show.get(position);
// 这里是客户端
try {
BluetoothSocket rfcommSocketToServiceRecord = device.createRfcommSocketToServiceRecord(uuid);
rfcommSocketToServiceRecord.connect(); //连接
OutputStream outputStream = rfcommSocketToServiceRecord.getOutputStream();
outputStream.write("哈哈哈哈哈哈".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_open: //打开蓝牙
break;
case R.id.bt_scan: //搜索
break;
case R.id.bt_close: //关闭
break;
case R.id.bt_show: //显示匹配
break;
}
}
- 获取本机蓝牙, 通过蓝牙管理者获取
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); //获得蓝牙管理者
adapter = manager.getAdapter();//得到蓝牙适配器 adapter为全局变量为:BluetoothAdapter adapter;
- 注册广播 和解除注册实现
5.1 注册广播
myRecevier=new MyRecevier(); //全局 广播接收者
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//扫描到远程设备
registerReceiver(myRecevier,intentFilter);
5.2 在Activity声明周期的onDestroy中解除注册
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver); //解除注册
}
- 功能实现
6.1 打开蓝牙(使用隐示意图)
private void open() {
Intent intent = new Intent();
intent.setAction(BluetoothAdapter.ACTION_REQUEST_ENABLE); //设置蓝牙可用
intent.setAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); //设置被扫描
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 200); // 设置被扫描的时长
startActivity(intent);
// TODO 5. 创建ServerSocket监听客户端的连接
new Thread(new Runnable() {
@Override
public void run() {
try {
BluetoothServerSocket serverSocket = adapter.listenUsingInsecureRfcommWithServiceRecord(adapter.getName(), uuid);
while (true) { //等待客户端的连接, 每连接到一个就要开启线程
BluetoothSocket socket = serverSocket.accept();
new ServerThread(MainActivity.this, socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
6.2 显示已配对的设备
private void show() {
list_show.clear();
Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices();//获得已经配对的蓝牙设备
list_show.addAll(bondedDevices);
show_myAdapter.notifyDataSetChanged();
}
6.3 关闭蓝牙
private void close() {
adapter.disable(); // 关闭蓝牙
}
6.4 搜索附近设备
private void search() {
list_show.clear();
adapter.startDiscovery(); // 开始搜索
// adapter.cancelDiscovery(); // 停止搜索
}
- 广播接收者: 接收系统发送的广播, 扫描到设备
class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { //扫描到一个
// 获取远程蓝牙设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 放到list_showAll中
list_showAll.add(device);
showAll_myAdapter.notifyDataSetChanged(); // 通知适配器更新数据
}
}
}
二. 全部代码的实现
1. 布局文件
activity_main布局文件
<?xml version="1.0" encoding="utf-8"?>
<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:gravity="center_horizontal"
android:orientation="vertical"
android:padding="5dp"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/bt_open"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="打开蓝牙" />
<Button
android:id="@+id/bt_scan"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="扫描蓝牙" />
<Button
android:id="@+id/bt_close"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="关闭蓝牙" />
</LinearLayout>
<Button
android:id="@+id/bt_show"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:text="查看已经配对的设备" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#615353"
android:gravity="center_vertical"
android:text="已经配对的蓝牙设备"
android:textColor="#ffffff" />
<ListView
android:id="@+id/lv2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="10" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="1dp"
android:layout_weight="1"
android:background="#615353"
android:gravity="center_vertical"
android:text="扫描到设备"
android:textColor="#ffffff" />
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="10" />
</LinearLayout>
2. MainActivity中
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* 1.蓝牙:BlueTooth 短距离数据传输
* 2.应用:蓝牙耳机 蓝牙音箱
* 3.功能:
* 打开蓝牙:
* 关闭蓝牙:
* 扫描附近的蓝牙设备:
* 配对蓝牙:
* 传输文件:
* 4.打开蓝牙:隐式意图打开蓝牙
* 5.关闭蓝牙:adapter.disable()
* 6.扫描:adapter.startDiscovery()
* 7.配对:device.createBond()
* 8.获取配对的设备:adapter.getBonedDevices()
* 9.传输数据
* (1)客户端
* (2)服务端:
* 一般保证蓝牙设备打开状态,不要点击关闭
**/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private UUID uuid = UUID.fromString("00001106-0000-1000-8000-00805F9B34FB");//蓝牙通讯规范
private Button bt_open;
private Button bt_scan;
private Button bt_close;
private Button bt_show;
private ListView lv_show; //显示已匹配的蓝牙设备
private ListView lv_showAll; //显示所有可匹配的蓝牙设备
List<BluetoothDevice> list_show = new ArrayList<>(); // 用于存放已匹配的蓝牙设备
List<BluetoothDevice> list_showAll = new ArrayList<>(); //用于存放所有可匹配的蓝牙设备
MyAdapter showAll_myAdapter, show_myAdapter;
BluetoothAdapter adapter; //蓝牙适配器
MyReceiver myReceiver; //广播
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// TODO 1:动态收授权
initEmpower(); //授权
// TODO 2:初始化视图
initView();
// TODO 3:获得本机蓝牙
getLocalBluetooth();
// TODO 4: 注册广播 和解除广播, 解除广播在onDestroy()中进行
registerRec();
}
/**
* 注册广播
*/
private void registerRec() {
myReceiver = new MyReceiver();// 创建广播接收者, 全局变量, 方便用于注销
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND); //扫描到远程设备
registerReceiver(myReceiver, intentFilter); // 注册
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver); //解除注册
}
/**
* 获得本机蓝牙
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void getLocalBluetooth() {
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); //获得蓝牙管理者
adapter = manager.getAdapter();//得到蓝牙适配器
}
/**
* 授权
*/
private void initEmpower() {
// 数组用于存放权限
String[] strings = new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 判断系统版本是否是6.0以上
// 判断权限是否授权
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(strings, 101); //动态获取权限
}
}
}
private void initView() {
bt_open = (Button) findViewById(R.id.bt_open);
bt_open.setOnClickListener(this);
bt_scan = (Button) findViewById(R.id.bt_scan);
bt_scan.setOnClickListener(this);
bt_close = (Button) findViewById(R.id.bt_close);
bt_close.setOnClickListener(this);
bt_show = (Button) findViewById(R.id.bt_show);
bt_show.setOnClickListener(this);
lv_show = (ListView) findViewById(R.id.lv2);
lv_showAll = (ListView) findViewById(R.id.lv);
//显示已匹配的蓝牙设备
show_myAdapter = new MyAdapter(this, list_show);
lv_show.setAdapter(show_myAdapter);
//显示所有可匹配的蓝牙设备
showAll_myAdapter = new MyAdapter(this, list_showAll);
lv_showAll.setAdapter(showAll_myAdapter);
// 可匹配设备中 点击蓝牙设备 发起配对请求
lv_showAll.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
list_showAll.get(position).createBond(); //发送配对请求
}
});
// 已匹配设备中 点击蓝牙设备 发送信息, 或进行其他操作, 这里只发送信息
lv_show.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final BluetoothDevice device = list_show.get(position);
// 这里是客户端
try {
BluetoothSocket rfcommSocketToServiceRecord = device.createRfcommSocketToServiceRecord(uuid);
rfcommSocketToServiceRecord.connect(); //连接
OutputStream outputStream = rfcommSocketToServiceRecord.getOutputStream();
outputStream.write("哈哈哈哈哈哈".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_open: //打开蓝牙
open();
break;
case R.id.bt_scan: //搜索
search();
break;
case R.id.bt_close: //关闭
close();
break;
case R.id.bt_show: //显示匹配
show();
break;
}
}
/**
* 打开蓝牙 使用隐示意图打开蓝牙
*/
private void open() {
Intent intent = new Intent();
intent.setAction(BluetoothAdapter.ACTION_REQUEST_ENABLE); //设置蓝牙可用
intent.setAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); //设置被扫描
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 200); // 设置被扫描的时长
startActivity(intent);
// TODO 5. 创建ServerSocket监听客户端的连接
new Thread(new Runnable() {
@Override
public void run() {
try {
BluetoothServerSocket serverSocket = adapter.listenUsingInsecureRfcommWithServiceRecord(adapter.getName(), uuid);
while (true) { //等待客户端的连接, 每连接到一个就要开启线程
BluetoothSocket socket = serverSocket.accept();
new ServerThread(MainActivity.this, socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 显示已配对的设备
*/
private void show() {
list_show.clear();
Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices();//获得已经配对的蓝牙设备
list_show.addAll(bondedDevices);
show_myAdapter.notifyDataSetChanged();
}
/**
* 关闭
*/
private void close() {
adapter.disable(); // 关闭蓝牙
}
/**
* 搜索
*/
private void search() {
list_show.clear();
adapter.startDiscovery(); // 开始搜索
// adapter.cancelDiscovery(); // 停止搜索
}
/**
* 广播接收者: 接收系统发送的广播, 扫描到设备
*/
class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { //扫描到一个
// 获取远程蓝牙设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 放到list_showAll中
list_showAll.add(device);
showAll_myAdapter.notifyDataSetChanged(); // 通知适配器更新数据
}
}
}
}
3. MyAdapter 中
import android.bluetooth.BluetoothDevice;
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;
public class MyAdapter extends BaseAdapter {
Context context;
List<BluetoothDevice> list;
public MyAdapter(Context context, List<BluetoothDevice> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.layout_item, parent, false);
holder.tv_name = convertView.findViewById(R.id.tv_name);
holder.tv_address = convertView.findViewById(R.id.tv_address);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
holder.tv_name.setText(list.get(position).getName());
holder.tv_address.setText(list.get(position).getAddress());
return convertView;
}
class ViewHolder {
TextView tv_name, tv_address;
}
}
4. ServerThread中 (线程)
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothSocket;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
public class ServerThread extends Thread {
Activity context;
BluetoothSocket socket;
public ServerThread(Activity context, BluetoothSocket socket) {
this.context = context;
this.socket = socket;
}
@Override
public void run() {
super.run();
// 回到主线程
context.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "有人要给我发数据", Toast.LENGTH_SHORT).show();
}
});
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
while ((len = inputStream.read(bytes)) != -1){
final String s = new String(bytes, 0, len);
context.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "接收的数据是: "+s, Toast.LENGTH_SHORT).show();
}
});
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
5. 适配器布局
<?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:gravity="center_horizontal"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
</LinearLayout>
编辑时间: 2019年7月23日21:29:42
Demo下载