經典藍牙的基本操作

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

}


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