经典蓝牙的基本操作

需要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;
        }
    }

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章