Android藍牙開發——經典藍牙的連接

1.藍牙的基本操作

藍牙權限

android.permission.BLUETOOTH
//允許程序連接到已配對的藍牙設備,請求連接/接收連接/傳輸數據需要改權限, 主要用於對配對後進行操作.
android.permission.BLUETOOTH_ADMIN
//允許程序發現和配對藍牙設備, 該權限用來管理藍牙設備, 有了這個權限, 應用才能使用本機的藍牙設備.

BluetoothAdapter

BluetoothAdapter代表了移動設備的本地的藍牙適配器, 通過該藍牙適配器可以對藍牙進行基本操作

//獲取藍牙適配器對象.
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

開關藍牙(推薦請求用戶打開)

//直接打開
mBluetoothAdapter.enable();
//請求用戶打開藍牙
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent);
//關閉藍牙
mBluetoothAdapter.disable();

藍牙是否可用

//藍牙是否可用,返回boolean true可用 false不可用
mBluetoothAdapter.isEnabled();

藍牙可被發現及時間設置

//可被發現
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600);
//默認120startActivity(intent);

搜索藍牙

藍牙搜索在android6.0以後需要加上一個模糊定位的權限,並動態申請,雖然有些手機不加權限也能搜索到藍牙設備,

但是大多會出現藍牙一直等待搜索,藍牙列表無法彈出。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

//掃描藍牙的方法,返回boolean//開始掃描
mBluetoothAdapter.startDiscovery();
//是否正在掃描
mBluetoothAdapter.isDiscovering();
//停止掃描
mBluetoothAdapter.cancelDiscovery();

startDiscovery()是一個異步方法,在搜索藍牙設備的過程中,系統可能會發送以下三個廣播:

ACTION_DISCOVERY_START(開始搜索)

ACTION_DISCOVERY_FINISHED(搜索結束)

ACTION_FOUND(找到設備)。

藍牙地址

//獲取本地藍牙地址 返回String
mBluetoothAdapter.getAddress();
//檢查藍牙地址 藍牙地址字母必須大寫, 例如 : "00:43:A8:23:10:F0";
mBluetoothAdapter.checkBluetoothAddress(String address);

獲取遠程藍牙設備

BluetoothDevice 代表了一個遠程的藍牙設備, 通過這個類可以查詢遠程設備的物理地址, 名稱, 連接狀態等信息;

//作用 : 根據藍牙的物理地址獲取遠程的藍牙設備, 如果地址不合法, 就會產生異常;
//返回值 : 獲取到的BluetoothDevice對象;
public BluetoothDevice getRemoteDevice(String address);

藍牙名字

//設置名字
mBluetoothAdapter.setName();
//得到名字
mBluetoothAdapter.getName();

創建監聽

//創建一個監聽Rfcommon端口的藍牙監聽, 使用accept()方法監聽, 並獲取BluetoothSocket對象; 
// 該系統會根據一個服務名稱(name)和唯一的識別碼(uuid)來創建一個SDP服務, 
// 遠程藍牙設備可以根據唯一的UUID來連接這個SDP服務器;
public BluetoothServerSocket listenUsingRfcommonWithServiceRecord(String name, UUID uuid);
// 參數 : name : SDP服務器名稱, UUID, SDP記錄下的UUID;
// 返回值 : 正在監聽藍牙端口;

UUID

在藍牙中,每個服務和服務屬性都唯一地由全局唯一標識符,Universally Unique Identifier(UUID)來校驗。UUID相當於Socket的端口,

而藍牙地址相當於Socket的IP。兩個藍牙設備進行連接時需要使用同一個UUID, 這是一個服務的唯一標識。

於普通藍牙適配器和android手機藍牙模塊連接的UUID

//手機連接的UUID
//設備連接的UUID由廠商決定。
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

其他UUID可看http://blog.csdn.net/spmno/article/details/6931941

藍牙連接
藍牙客戶端 BluetoothSocket,藍牙服務端 BluetoothServerSocket 匹配成功然後使用connect()方法連接

2.具體代碼(掃描藍牙)沒有其他藍牙設備,使用兩部手機替代。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:onClick="startBluetooth"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="開啓藍牙"/>
    <Button
        android:onClick="searchBluetooth"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="搜索藍牙"/>
    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

兩個按鈕,一個ListView顯示藍牙設備名字

初始畫數據,設置適配器

ListView mListView;
BluetoothAdapter mBluetoothAdapter;
ArrayAdapter<String> mAdapter;
//存儲藍牙設備名字
List<String> deviceNames = new ArrayList<>();
//存儲藍牙設備
List<BluetoothDevice> devices = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    mListView = (ListView) findViewById(R.id.listview);
    mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, deviceNames);
    mListView.setAdapter(mAdapter);
}

開啓藍牙

//按鈕點擊事件 開啓藍牙
public void startBluetooth(View view) {
    //設置藍牙可被發現
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    //設置可被發現的時間 1200    intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,1200);
    startActivity(intent);
}

搜索藍牙 需要動態權限

//搜索藍牙
public void searchBluetooth(View view) {
    //藍牙開啓狀態
    if (mBluetoothAdapter.isEnabled()) {
        //動態權限
        if (Build.VERSION.SDK_INT > 23) {
            int check = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
            if (check == PackageManager.PERMISSION_GRANTED) {
                //開始掃描
                startSearch();
            } else {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1);
            }
        } else {
            startSearch();
        }
        startSearch();
    } else {
        //如果藍牙沒開啓
        startBluetooth(view);
    }
}
//開始搜索藍牙
private void startSearch() {
    //如果沒在搜索
    if (!mBluetoothAdapter.isDiscovering()) {
        //搜索藍牙設備
        mBluetoothAdapter.startDiscovery();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode ==1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        startSearch();
    } else {
        Toast.makeText(this, "必須獲取地理位置才能掃描藍牙設備", Toast.LENGTH_SHORT).show();
    }
}

記得在AndroidManifest文件中在加上模糊地理位置權限

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

掃描藍牙時,系統會發送廣播,通過廣播接收者獲得掃描到的藍牙設備

//廣播接收者
private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //如果發現藍牙設備
        if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
            //BluetoothDevice 代表了一個遠程的藍牙設備, 通過這個類可以查詢遠程設備的狀態信息;
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            //取得藍牙設備的名字(名字可能爲空,空則設置名字爲匿名) 以便顯示到ListView
            String name = device.getName() == null ? "匿名" : device.getName();
            deviceNames.add(name);
            mAdapter.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);
}

暫時效果


3.具體代碼(藍牙連接)

兩個藍牙手機鏈接,需要有服務器客戶端

UUID

//手機連接的UUID
//設備連接的UUID由廠商決定。
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

服務端

class BluetoothServer extends Thread {
    @Override
    public void run() {
        super.run();
        //服務
        BluetoothServerSocket bluetoothServerSocket;
        try {
            bluetoothServerSocket = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
                    mBluetoothAdapter.getName(),uuid);
            BluetoothSocket socket = bluetoothServerSocket.accept();
            while (socket.isConnected()) {
                InputStream inputStream = socket.getInputStream();
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = inputStream.read(buffer)) != -1) {
                    Log.e("TAG",new String(buffer,0,len,"utf-8"));
                }
            }
            //將服務端關閉,不可能1對多,所以,只要獲取了客戶端,那麼服務端就可以關閉了
            bluetoothServerSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

然後在開啓藍牙後開啓服務,即在searchBluetooh中調用startServer方法

BluetoothServer server;
public void startServer() {
    if (server == null) {
        server = new BluetoothServer();
        server.start();
    }
}

客戶端

MainActivity 實現OnItemClickListener 然後重寫方法

mListView.setOnItemClickListener(this);

//客戶端 搜索出藍牙後,ListView Item點擊事件 鏈接服務端
@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.createInsecureRfcommSocketToServiceRecord(uuid);
                //鏈接
                socket.connect();
                OutputStream os = socket.getOutputStream();
                os.write("連接藍牙服務器成功".getBytes());
                os.flush();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }.start();
}

以上代碼都寫在一個工程中,一步手機既可以做客戶端,又可以做服務端。


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