Android藍牙開發知識總結(搜索,配對,連接,數據互傳)

         最近公司開發需要用到藍牙,花了大約一天的時間總結整理了一下。主要是爲了以後自己方便看。有需要的朋友可以看下。歡迎一起討論。後面會帶上demo。裏面是實現了藍牙搜索,配對,連接,數據互傳。

首先需要AndroidManifest.xml文件中添加操作藍牙的權限。

<uses-permission android:name="android.permission.BLUETOOTH" />允許程序連接到已配對的藍牙設備。 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />允許程序發現和配對藍牙設備。
Android 6.0後需要加上 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

一、我們需要用到了幾個相關的類:

    1.BluetoothAdapter 顧名思義,藍牙適配器,直到我們建立bluetoothSocket連接之前,都要不斷操作它

      BluetoothAdapter裏的方法很多,常用的有以下幾個:

      cancelDiscovery() 根據字面意思,是取消發現,也就是說當我們正在搜索設備的時候調用這個方法將不再繼續搜索

      disable()關閉藍牙

      enable()打開藍牙,這個方法打開藍牙不會彈出提示,更多的時候我們需要問下用戶是否打開,以下這兩行代碼同樣是打開藍牙,不過會提示用戶:

Intemtenabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enabler,reCode);//同startActivity(enabler);

      getAddress()獲取本地藍牙地址

      getDefaultAdapter()獲取默認BluetoothAdapter,實際上,也只有這一種方法獲取BluetoothAdapter

      getName()獲取本地藍牙名稱

      getRemoteDevice(String address)根據藍牙地址獲取遠程藍牙設備

      getState()獲取本地藍牙適配器當前狀態(感覺可能調試的時候更需要)

      isDiscovering()判斷當前是否正在查找設備,是返回true

      isEnabled()判斷藍牙是否打開,已打開返回true,否則,返回false

     listenUsingRfcommWithServiceRecord(String name,UUID uuid)根據名稱,UUID創建並返回BluetoothServerSocket,這是創建BluetoothSocket服務器端的第一步

      startDiscovery()開始搜索,這是搜索的第一步

    2.BluetoothDevice看名字就知道,這個類描述了一個藍牙設備

      createRfcommSocketToServiceRecord(UUIDuuid)根據UUID創建並返回一個BluetoothSocket


這個方法也是我們獲取BluetoothDevice的目的——創建BluetoothSocket
這個類其他的方法,如getAddress(),getName(),同BluetoothAdapter

   3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不過了,既然是Socket,方法就應該都差不多,這個類一種只有三個方法

兩個重載的accept(),accept(inttimeout)兩者的區別在於後面的方法指定了過時時間,需要注意的是,執行這兩個方法的時候,直到接收到了客戶端的請求(或是過期之後),都會阻塞線程,應該放在新線程裏運行!


還有一點需要注意的是,這兩個方法都返回一個BluetoothSocket,最後的連接也是服務器端與客戶端的兩個BluetoothSocket的連接

      close()這個就不用說了吧,關閉資源!

    4.BluetoothSocket,跟BluetoothServerSocket相對,是客戶端

      一共5個方法,不出意外,都會用

      close(),關閉

      connect()連接

      getInptuStream()獲取輸入流

      getOutputStream()獲取輸出流

      getRemoteDevice()獲取遠程設備,這裏指的是獲取bluetoothSocket指定連接的那個遠程藍牙設備

二、直接上代碼。

MainActivity主要開啓藍牙,需要動態獲取權限的獲取權限。藍牙傳遞數據分客戶端和服務端,這裏給一個入口,進客戶端和服務端。
package com.qtwl.bluetooth;

import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener {
    BluetoothAdapter mBluetoothAdapter;
    public static final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button service = findViewById(R.id.service);
        Button clent = findViewById(R.id.clent);

        service.setOnClickListener(this);
        clent.setOnClickListener(this);

        if (Build.VERSION.SDK_INT >= 6.0) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1000);
        }

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter != null) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                // 設置藍牙可見性,最多300秒
                intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                startActivity(intent);
            }
        } else {
            Toast.makeText(this, "沒有藍牙模塊", Toast.LENGTH_SHORT).show();
        }

    }

    @Override
    public void onClick(View view) {
        if (mBluetoothAdapter == null) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                // 設置藍牙可見性,最多300秒
                intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                startActivity(intent);
                return;
            }
        }

        Intent intent = null;


//        要在兩臺設備上的應用之間創建連接,必須同時實現服務器端和客戶端機制,因爲其中一臺設備必須開放服務器套接字,而另一臺設備必須發起連接(使用服務器設備的 MAC 地址發起連接)。
//        服務器設備和客戶端設備分別以不同的方法獲得需要的 BluetoothSocket。服務器將在傳入連接被接受時收到套接字。 客戶端將在其打開到服務器的 RFCOMM 通道時收到該套接字。
        switch (view.getId()) {
            case R.id.service://服務端
                intent = new Intent(this, ServiceActivity.class);
                break;

            case R.id.clent://客戶端
                intent = new Intent(this, ClentActivity.class);
                break;

        }
        startActivity(intent);
    }
}

  

ServiceActivity服務端的代碼。
package com.qtwl.bluetooth;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

/**
 * 連接爲服務器
 * 當您需要連接兩臺設備時,其中一臺設備必須通過保持開放的 BluetoothServerSocket 來充當服務器。
 * 1.通過調用 listenUsingRfcommWithServiceRecord(String, UUID) 獲取 BluetoothServerSocket。
 * 當客戶端嘗試連接此設備時,它會攜帶能夠唯一標識其想要連接的服務的 UUID。 兩個 UUID 必須匹配,在下一步中,連接纔會被接受。
 * 通過調用 accept() 開始偵聽連接請求。
 * 除非您想要接受更多連接,否則請調用 close()。
 * 這將釋放服務器套接字及其所有資源,但不會關閉 accept() 所返回的已連接的 BluetoothSocket。 與 TCP/IP 不同,RFCOMM 一次只允許每個通道有一個已連接的客戶端,因此大多數情況下,在接受已連接的套接字後立即在 BluetoothServerSocket 上調用 close() 是行得通的。
 */


public class ServiceActivity extends BaseActivity {

    BluetoothServerSocket mmServerSocket;
    TextView title;
    TextView tvReceive;

    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            tvReceive.setText(tvReceive.getText() + "\n" + msg.obj.toString());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service);
        title = findViewById(R.id.tv_service_isStart);
        tvReceive = findViewById(R.id.tv_service);


        try {
            mmServerSocket = BluetoothAdapter.getDefaultAdapter().listenUsingRfcommWithServiceRecord("com.qtwl.bluetooth", UUID.fromString(MainActivity.MY_UUID));
            title.setText("藍牙服務器開啓成功");
        } catch (IOException e) {
            e.printStackTrace();
            title.setText("藍牙服務器開啓失敗");
        }

        //寫一個線程來做服務器,等待客戶端來連接它
        new Thread() {
            @Override
            public void run() {
                super.run();
                while (true) {
                    try {
                        showToast("等待別人來連接");
                        BluetoothSocket socket = mmServerSocket.accept();
                        if (socket != null) {
                            Message msg = Message.obtain();
                            InputStream is = socket.getInputStream();
                            byte[] buffer = new byte[1024 * 128];
                            int count = is.read(buffer);

                            msg.obj = new String(buffer, 0, count, "utf-8");
                            handler.sendMessage(msg);

                            OutputStream os = socket.getOutputStream();
                            os.write("服務器收到了".getBytes("UTF-8"));
                            showToast("服務器做了返回");

                        }
                    } catch (IOException e) {
                        break;
                    }
                }
            }
        }.start();
    }
}

客戶端的代碼,主要是搜索附近的藍牙,並配對。

package com.qtwl.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 作爲客戶端
 */
public class ClentActivity extends BaseActivity {
    ListView mLvBle;//藍牙列表
    Button search;//搜索附近藍牙

    private List<BluetoothDevice> mDeviceList = new ArrayList<>();//藍牙列表
    private BleAdapter mAdapter;

    BluetoothAdapter mBluetoothAdapter;

    /**
     * 廣播接收者
     */
    private BroadcastReceiver bTReceive = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                Log.i("BroadcastReceiver", "BluetoothDevice.ACTION_FOUND  device = "+device.getName()+"  "+device.getAddress());
                for (BluetoothDevice d : mDeviceList) {
                    if (TextUtils.isEmpty(d.getAddress()) || TextUtils.isEmpty(device.getAddress()) || d.getAddress().equalsIgnoreCase(device.getAddress())) {
                        return;
                    }
                }
                mDeviceList.add(device);
                mAdapter.notifyDataSetChanged();
            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                Log.e("BroadcastReceiver", "BluetoothDevice.ACTION_BOND_STATE_CHANGED");
                BluetoothDevice blnDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                setProgressMsg("正在配對..");
                switch (blnDevice.getBondState()) {
                    case BluetoothDevice.BOND_BONDING://正在配對
                        setProgressMsg("正在配對..");
                        break;

                    case BluetoothDevice.BOND_BONDED://配對結束
                        mAdapter.notifyDataSetChanged();
                        setProgressMsg("配對完成,開始連接..");
                        startActivity(blnDevice);
                        break;

                    case BluetoothDevice.BOND_NONE://取消配對/未配對
                        showToast("配對失敗!");
                        for (BluetoothDevice d : mDeviceList) {
                            if (d.getName().equalsIgnoreCase(blnDevice.getName())) {
                                mAdapter.notifyDataSetChanged();
                                return;
                            }
                        }
                        dismissProgressDialog();
                    default:
                        break;
                }
            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                Log.e("BroadcastReceiver", "BluetoothAdapter.ACTION_BOND_STATE_CHANGED");
                Toast.makeText(context, "BluetoothAdapter.ACTION_BOND_STATE_CHANGED", Toast.LENGTH_SHORT).show();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_clent);
        mLvBle = findViewById(R.id.lv_ble);//藍牙列表
        search = findViewById(R.id.btn_search);//搜索附近藍牙

         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        registerBTReceiver();// 註冊藍牙相關的各種廣播

        //給藍牙列表設置數據
        mAdapter = new BleAdapter(this);
        mAdapter.setData(mDeviceList);
        mLvBle.setAdapter(mAdapter);
        mLvBle.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                if (mBluetoothAdapter != null)
                    mBluetoothAdapter.cancelDiscovery();
                BluetoothDevice device = mDeviceList.get(i);
                bondBT(device);
            }
        });

        search.setOnClickListener(new View.OnClickListener() {//點擊搜索按鈕
            @Override
            public void onClick(View view) {
                if (mBluetoothAdapter.isDiscovering()) {
                    return;
                }
                mBluetoothAdapter.startDiscovery();
            }
        });
    }

    /**
     * 註冊廣播
     */
    public void registerBTReceiver() {
        // 設置廣播信息過濾
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);   //發現藍牙事件
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//藍牙掃描結束事件
//        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//狀態改變
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        // 註冊廣播接收器,接收並處理搜索結果
        registerReceiver(bTReceive, intentFilter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        mBluetoothAdapter.cancelDiscovery();
    }

    @Override
    protected void onResume() {
        super.onResume();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //註銷廣播
        unregisterReceiver(bTReceive);
    }

    /**
     * 綁定藍牙
     */
    private void bondBT(BluetoothDevice device) {
        showProgressDialog("配對藍牙開始");
        // 搜索藍牙設備的過程佔用資源比較多,一旦找到需要連接的設備後需要及時關閉搜索
        // 獲取藍牙設備的連接狀態
        int connectState = device.getBondState();
        switch (connectState) {
            case BluetoothDevice.BOND_NONE: // 未配對
                setProgressMsg("開始配對");
                try {
                    Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
                    createBondMethod.invoke(device);
                } catch (Exception e) {
                    showToast("配對失敗!");
                    dismissProgressDialog();
                    e.printStackTrace();
                }
                break;

            case BluetoothDevice.BOND_BONDED: // 已配對
                //進入連接界面
                startActivity(device);
                break;
        }
    }

    //如果配對好了,就跳轉到發消息界面
    private void startActivity(BluetoothDevice device) {
        dismissProgressDialog();
        Intent intent = new Intent(this, ClentSendActivity.class);
        intent.putExtra("device", device);

        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(intent);
    }





}
ClentSendActivity客戶端連接的代碼。客戶端連接服務器,併發送數據,然後收到服務器返回的數據。
package com.qtwl.bluetooth;

import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

/**
 * 客戶端發消息
 */
public class ClentSendActivity extends BaseActivity {
    private BluetoothDevice device;
    private BluetoothSocket bTSocket;
    TextView btNmae;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_clent_send);
        btNmae = findViewById(R.id.bluetooth_name);
        device = getIntent().getParcelableExtra("device");
        btNmae.setText(device.getName() + "  " + device.getAddress());
        connectDevice();

    }

    /**
     * 連接藍牙
     */
    private void connectDevice() {
        showProgressDialog("正在連接藍牙");
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    bTSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(MainActivity.MY_UUID)); //創建一個Socket連接:只需要服務器在註冊時的UUID號
                    bTSocket.connect(); //連接
                    showToast("連接成功");
                    dismissProgressDialog();

                    OutputStream os = bTSocket.getOutputStream();
                    os.write("test 測試數據".getBytes("UTF-8"));
                    showToast("寫數據成功");

                    InputStream is = bTSocket.getInputStream();
                    byte[] buffer = new byte[1024 * 128];
                    int count = is.read(buffer);
                    String string = new String(buffer, 0, count, "utf-8");
                    showToast("讀到數據 string = " + string);

                } catch (IOException e) {
                    showToast("連接異常,請退出本界面再次嘗試!");
                    dismissProgressDialog();
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

主要功能上面都有,這個是上面的 源碼

 

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