需要3个权限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<?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:orientation="vertical"
tools:context="com.example.bluetooth.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="open"
android:text="直接打开蓝牙" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="request"
android:text="请求用户打开蓝牙" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="close"
android:text="关闭蓝牙" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="setName"
android:text="设置蓝牙名称" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="discover"
android:text="打开可被发现" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="getBound"
android:text="获取已配对的设备" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="scan"
android:text="开始扫描" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="startServer"
android:text="建立连接" />
</LinearLayout>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
package com.example.bluetooth;
import android.Manifest;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
BluetoothAdapter bluetoothAdapter;
ListView lv;
// 所有扫描的蓝牙名称的集合
List<String> deviceNames = new ArrayList<>();
// 所有扫描的蓝牙设备的集合
List<BluetoothDevice> devices = new ArrayList<>();
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取蓝牙适配器
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 找到ListView控件
lv = (ListView) findViewById(R.id.lv);
// 创建一个适配器
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, deviceNames);
lv.setAdapter(adapter);
// 设置ListView的点击事件去请求服务端
lv.setOnItemClickListener(this);
}
// 点击按钮直接打开蓝牙
public void open(View view) {
// 直接打开蓝牙(不推荐使用的)第一次打开需要获取权限 要添加权限 BLUETOOTH_ADMIN 和 BLUETOOTH
bluetoothAdapter.enable();
}
// 点击按钮请求用户打开蓝牙
public void request(View view) {
// 请求用户打开蓝牙(推荐使用的方法)每次打开都需要获取权限
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent);
}
// 点击按钮关闭蓝牙
public void close(View view) {
// 关闭直接调用disable方法
bluetoothAdapter.disable();
}
// 点击按钮设置蓝牙名称
public void setName(View view) {
// 创建一个对话框
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 创建一个输入控件
final EditText et = new EditText(this);
// 显示蓝牙名称
et.setText(bluetoothAdapter.getName());
// 把输入控件设置到对话框内
builder.setView(et);
// 设置对话框标题
builder.setTitle("设置蓝牙名称");
// 设置取消确定按钮
builder.setNegativeButton("取消", null);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String name = et.getText().toString();
if (TextUtils.isEmpty(name)) {
Toast.makeText(MainActivity.this, "蓝牙名称不能为空", Toast.LENGTH_SHORT).show();
} else {
bluetoothAdapter.setName(name);
}
}
}).create().show();
}
// 点击按钮可被发现或扫描
public void discover(View view) {
// 使用意图来操作 可被发现
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
// 可被发现的默认时间是120秒(可以被改,这里改成600秒)
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600);
startActivity(intent);
}
// 点击按钮获取已配对过的设备
public void getBound(View view) {
// 获取已配对的蓝牙设备
Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
// set集合里面去数据用迭代器或者foreach,不能使用for循环,因为没有索引号
for (BluetoothDevice bondedDevice : bondedDevices) {
// 这里就打印下
Log.e("TAG", "-----------" + bondedDevice.getName());
}
}
// 点击按钮开始扫描附近蓝牙设备
public void scan(View view) {
// 扫描蓝牙必须是打开状态,先判断蓝牙的状态
if (bluetoothAdapter.isEnabled()) {
// 打开状态 (6.0以上,扫描必须动态获取地理位置权限)
if (Build.VERSION.SDK_INT >= 23) {
int check = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
if (check == PackageManager.PERMISSION_GRANTED) {
startScan();
} else {
// 请求权限(执行完就会执行权限请求结果的方法onRequestPermissionsResult())
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
}
} else {
// 开始扫描
startScan();
}
} else {
// 关闭状态
request(view);
}
}
private void startScan() {
// 以广播的方式来发送蓝牙设备
// 判断蓝牙是否正在扫描中
if (!bluetoothAdapter.isDiscovering()) {
// 开始扫描
bluetoothAdapter.startDiscovery();
}
// 停止扫描
//bluetoothAdapter.cancelDiscovery();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 判断结果(requestCode要与上面的相同,grantResults[0]是数组0坐标位置的权限)
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 开始扫描
startScan();
} else {
Toast.makeText(this, "由于android版本高于6.0必须获取地理位置才能扫描蓝牙", Toast.LENGTH_SHORT).show();
}
}
// 通过广播接收者来接收蓝牙设备
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 蓝牙设备被发现的监听
if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
// 获取到其它的蓝牙设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 从蓝牙设备中获取具体的数据,获取蓝牙的名称(如果没有名称就显示匿名)
String name = device.getName() == null ? "匿名" : device.getName();
// 把蓝牙名称添加到集合
deviceNames.add(name);
adapter.notifyDataSetChanged();
// 把蓝牙设备添加到集合
devices.add(device);
}
}
};
@Override
protected void onResume() {
super.onResume();
// 注册广播接受者
registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
@Override
protected void onPause() {
super.onPause();
// 注销广播接收者
unregisterReceiver(receiver);
}
// 点击按钮建立连接
public void startServer(View view) {
if (server == null) {
server = new MyServer();
// 开启线程
server.start();
}
}
MyServer server;
// 手机连接的UUID(可以随机获取),设备连接非UUID由厂商决定
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 点击列表请求服务器 从蓝牙设备集合中获取蓝牙设备
final BluetoothDevice device = devices.get(position);
// 需要耗时创建一个子线程
new Thread(){
@Override
public void run() {
super.run();
// 创建一个安全的连接
try {
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid);
// 必须要调socket.connect()才能打开流
socket.connect();
// 获取输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好!蓝牙服务器".getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
class MyServer extends Thread {
@Override
public void run() {
super.run();
BluetoothServerSocket bluetoothServerSocket;
try {
// 使用网络了所以要创建子线程
bluetoothServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(bluetoothAdapter.getName(), uuid);
// 等待客户的连接
BluetoothSocket socket = bluetoothServerSocket.accept();
// 如果是连接状态就一直创建流读取数据(客户端在ListView上显示,点击就请求服务器,接下来设置它的点击事件)
while (socket.isConnected()) {
// 从socket里面获取输入流读取数据
InputStream inputStream = socket.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
while ((len = inputStream.read(buf)) != -1) {
Log.e("TAG", "----------------" + new String(buf, 0, len, "utf-8"));
}
}
// 执行run方法后返回一个客户端,服务端就可以关闭了(蓝牙设备里面必须1对1所以可以关闭服务端,而TCP是1对多的所要服务器不能关闭)
bluetoothServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
// 这里等于null是为了下次还可以再建立服务端
bluetoothServerSocket = null;
}
}
}