Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android藍牙開發

Android藍牙開發

編輯:關於Android編程

想使用藍牙呢,首先得看手機是否支持,有些低配手機,可能就沒有內置藍牙模塊。當然,一般都會有,我們可以得到唯一的藍牙適配器,進行其他操作。

 

bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

然後,我們可做n多的事情。不要著急,後面會附上我的項目源碼,大牛見笑了。

 

 

/**
     * 開啟藍牙
     *
     * @param activity 上下文
     * @return 是否開啟成功
     */
    public static boolean openBluetooth(Activity activity) {
        //確認開啟藍牙
        if (!getInstance().isEnabled()) {
            //=默認120秒==============================================================
            //使藍牙設備可見,方便配對
            //Intent in = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            //in.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            //activity.startActivityForResult(in,Activity.RESULT_OK);
            //=1=============================================================
            //請求用戶開啟,需要提示
            //Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            //startActivityForResult(intent, RESULT_FIRST_USER);
            //=2=============================================================
            //程序直接開啟,不經過提示
            getInstance().enable();
        }
        //T.showLong(context, "已經開啟藍牙");
        return getInstance().isEnabled();
    }

    //關閉藍牙
    public static boolean closeBluetooth() {
        return getInstance().disable();
    }

    /**
     * 掃描已經配對的設備
     *
     * @return
     */
    public static ArrayList scanPairs() {
        ArrayList list = null;
        Set deviceSet = getInstance().getBondedDevices();
        if (deviceSet.size() > 0) {
            //存在已經配對過的藍牙設備
            list = new ArrayList<>();
            list.addAll(deviceSet);
        }
        return list;
    }

    //開始掃描
    public static void scan() {
        getInstance().startDiscovery();
    }

    //取消掃描
    public static void cancelScan() {
        if (getInstance().isDiscovering())
            getInstance().cancelDiscovery();

    }

    //藍牙配對
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean createBond(BluetoothDevice device) {
        return bond(device, "createBond");
        /*if (device.createBond()) {
            return device.setPairingConfirmation(true);
        }
        return false;*/
    }

    //解除配對
    public static boolean removeBond(BluetoothDevice device) {
        return bond(device, "removeBond");
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static boolean bond(BluetoothDevice device, String methodName) {
        Boolean returnValue = false;
        if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
            try {
                device.setPairingConfirmation(false);
                cancelPairingUserInput(device);
                Method removeBondMethod = BluetoothDevice.class.getMethod(methodName);
                returnValue = (Boolean) removeBondMethod.invoke(device);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return returnValue;
    }

    //取消配對
    public static boolean cancelBondProcess(BluetoothDevice device) {
        try {
            Method cancelBondMethod = BluetoothDevice.class.getMethod("cancelBondProcess");
            Boolean returnValue = (Boolean) cancelBondMethod.invoke(device);
            return returnValue.booleanValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    //取消用戶輸入
    public static boolean cancelPairingUserInput(BluetoothDevice device) {
        try {
            Method cancelPairingUserInputMethod = BluetoothDevice.class.getMethod("cancelPairingUserInput");
            Boolean returnValue = (Boolean) cancelPairingUserInputMethod.invoke(device);
            return returnValue.booleanValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

通過上面這段代碼,應道知道了吧,獲取到適配器後,可以得到當前手機已經配對的設備。同時可以開啟掃描(這個時間大概是12秒,異步的),掃描到設備和掃描完成系統會發廣播。故廣播接收代碼:

 

 

//注冊藍牙接收廣播
        if (!hasRegister) {
            hasRegister = true;
            //掃描結束廣播
            IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            //找到設備廣播
            filter.addAction(BluetoothDevice.ACTION_FOUND);
            filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
            filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
            filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
            registerReceiver(mMyReceiver, filter);
        }

private class MyReceiver extends 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);
                //搜索沒有配過對的藍牙設備
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    mListData.add(device);
                    dataAdapter.refreshData(mListData);
                } else {
                    T.showLong(TwoActivity.this, device.getName() + '\n' + device.getAddress() + " > 已發現");
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {   //搜索結束
                if (mListData.size() == 0) {
                    T.showLong(TwoActivity.this, "沒有發現任何藍牙設備");
                }
                progressDialog.dismiss();
                scan.setText("重新掃描");
            } else if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
                if (TwoActivity.this.position != -1) {
                    final BluetoothDevice device = mListData.get(TwoActivity.this.position);
                    com.dk.bluetooth.tools.T.showLong(TwoActivity.this, device + "  配對成功");
                    EventBus.getDefault().post(new com.dk.bluetooth.tools.MyEvent());
                }
            }
        }
    }

以上都不是重點,核心在線程通訊。首先得了解socket,java有這個,藍牙裡面也有,是BluetoothServerSocket和BluetoothSocket兩個,一個是服務器端的,一個是客戶端的。不了解的朋友建議先去百度java socket用法,超級簡單。

 

通訊需要建立信道,BluetoothServerSocket需要先啟動,監聽當前設備上的某UUID位置上的設備(阻塞到在此處),就跟windows的端口意思是一樣的。然後BluetoothSocket再啟動,根據對方的mac地址和對方監聽的UUID位置,啟動連接(也阻塞了),直到連上服務器了,就返回。服務器也一樣,直到有人來連接了,就返回。都會返回一個BluetoothSocket,然後從這個socket裡面獲取input和output流。服務器端了input流是客戶端的output流,另外一半也一樣。剩下的收發消息就是流的讀寫了,簡單吧。

下面貼出我的代碼,應為服務器端和客戶端啟動的方法不一樣,我分成了兩個線程,由於讀寫的功能一樣,我就共用了一套讀寫線程。根據這個思路看我的代碼。

 

/**
     * 初始化及啟動藍牙socket
     *
     * @param handler         UI消息傳遞對象
     * @param securityType    連接的安全模式
     * @param serverOrClient  客戶端或服務端
     * @param bluetoothDevice 服務器端設備
     */
    public BluetoothChatService(Handler handler, SecurityType securityType, ServerOrClient serverOrClient,
                                BluetoothDevice bluetoothDevice) {
        if (securityType != null)
            this.mSecurityType = securityType;
        if (serverOrClient != null)
            this.mServerOrClient = serverOrClient;
        if (bluetoothDevice != null)
            this.mBluetoothDevice = bluetoothDevice;
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = handler;
        start();
    }

    /**
     * 多線程同步修改狀態標識
     *
     * @param state
     */
    private synchronized void setState(int state) {
        mState = state;
        mHandler.obtainMessage(MESSAGE_TOAST_STATE_CHANGE, state, -1, null).sendToTarget();
    }

    /**
     * 多線程同步讀取狀態標識
     */
    public synchronized int getState() {
        return mState;
    }

    /**
     * 啟動服務
     */
    public void start() {
        start(null, null, null);
    }

    /**
     * 啟動服務
     *
     * @param securityType    連接的安全模式
     * @param serverOrClient  客戶端或服務端
     * @param bluetoothDevice 服務器端設備
     */
    public void start(SecurityType securityType, ServerOrClient serverOrClient, BluetoothDevice bluetoothDevice) {
        if (securityType != null)
            this.mSecurityType = securityType;
        if (this.mSecurityType == null) {
            if (DEBUG)
                Log.e(TAG, "mSecurityType cannot be null");
            mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "mSecurityType cannot be null").sendToTarget();
            return;
        }
        if (serverOrClient != null)
            this.mServerOrClient = serverOrClient;
        if (this.mServerOrClient == null) {
            if (DEBUG)
                Log.e(TAG, "mServerOrClient cannot be null");
            mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "mServerOrClient cannot be null").sendToTarget();
            return;
        }
        if (bluetoothDevice != null)
            this.mBluetoothDevice = bluetoothDevice;
        if (this.mBluetoothDevice == null) {
            if (DEBUG)
                Log.e(TAG, "mBluetoothDevice cannot be null");
            mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "mBluetoothDevice cannot be null").sendToTarget();
            return;
        }
        if (mState == STATE_NONE) {
            stop();
            if (this.mServerOrClient == ServerOrClient.SERVER) {
                if (mServerConnectThread == null) {
                    mServerConnectThread = new ServerConnectThread(this.mSecurityType);
                    mServerConnectThread.start();
                }
            } else if (this.mServerOrClient == ServerOrClient.CLIENT) {
                if (mClientConnectThread == null) {
                    mClientConnectThread = new ClientConnectThread(this.mSecurityType);
                    mClientConnectThread.start();
                }
            }
            setState(STATE_LISTEN);
        }
    }

    /**
     * 停止服務
     */
    public synchronized void stop() {
        try {
            if (mReadWriteThread != null) {
                mReadWriteThread.cancel();
                mReadWriteThread = null;
            }
            if (mServerConnectThread != null) {
                mServerConnectThread.cancel();
                mServerConnectThread = null;
            }
            if (mClientConnectThread != null) {
                mClientConnectThread.cancel();
                mClientConnectThread = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (DEBUG)
                Log.e(TAG, "BluetoothChatService -> stop() -> :failed " + e.getMessage());
            mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "BluetoothChatService -> stop() -> :failed").sendToTarget();
            mReadWriteThread = null;
            mServerConnectThread = null;
            mClientConnectThread = null;
        } finally {
            setState(STATE_NONE);
            System.gc();
        }
    }

    /**
     * 發送消息
     *
     * @param out 數據參數
     */
    public void write(String out) {
        if (TextUtils.isEmpty(out)) {
            if (DEBUG)
                Log.e(TAG, "please write something now");
            mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "BluetoothChatService -> write() -> :failed").sendToTarget();
            return;
        }
        ReadWriteThread r;
        synchronized (this) {
            if (mState != STATE_CONNECTED)
                return;
            r = mReadWriteThread;
        }
        r.write(out);
    }

    /**
     * 服務器端連接線程
     */
    @SuppressLint("NewApi")
    private class ServerConnectThread extends Thread {
        private BluetoothServerSocket mmServerSocket;
        private BluetoothSocket mmSocket = null;

        public ServerConnectThread(SecurityType securityType) {
            setName("ServerConnectionThread:" + securityType.getValue());
            BluetoothServerSocket tmp = null;
            try {
                if (securityType == SecurityType.SECURE) {
                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(SecurityType.SECURE.getValue(), MY_UUID_SECURE);
                } else if (securityType == SecurityType.INSECURE) {
                    tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SecurityType.INSECURE.getValue(),
                            MY_UUID_INSECURE);
                }
                if (tmp != null)
                    mmServerSocket = tmp;
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ServerConnectThread -> ServerConnectThread() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ServerConnectThread -> ServerConnectThread() -> :failed").sendToTarget();
                mmServerSocket = null;
                BluetoothChatService.this.stop();
            }
        }

        public void run() {
            try {
                // 正在連接
                setState(STATE_CONNECTING);
                //accept() 阻塞式的方法,群聊時,需要循環accept接收客戶端
                mmSocket = mmServerSocket.accept();
                connected(mmSocket);
            } catch (Exception e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ServerConnectThread -> run() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ServerConnectThread -> run() -> :failed").sendToTarget();
                BluetoothChatService.this.stop();
            }
        }

        public void cancel() {
            try {
                if (mmSocket != null) {
                    mmSocket.close();
                    mmSocket = null;
                }
                if (mmServerSocket != null) {
                    mmServerSocket.close();
                    mmServerSocket = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ServerConnectThread -> cancel() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ServerConnectThread -> cancel() -> :failed").sendToTarget();
                mmSocket = null;
                mmServerSocket = null;
                BluetoothChatService.this.stop();
            }
        }
    }

    // 客戶端連接線程
    private class ClientConnectThread extends Thread {
        private BluetoothSocket mmSocket;

        public ClientConnectThread(SecurityType securityType) {
            setName("ClientConnectThread:" + securityType.getValue());
            BluetoothSocket tmp = null;
            try {
                if (securityType == SecurityType.SECURE) {
                    tmp = mBluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
                    //Method m = mBluetoothDevice.getClass().getMethod("createRfcommSocket", int.class);
                    //tmp = (BluetoothSocket) m.invoke(mBluetoothDevice, 1);
                } else if (securityType == SecurityType.INSECURE) {
                    tmp = mBluetoothDevice.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);
                    //Method m = mBluetoothDevice.getClass().getMethod("createRfcommSocket", int.class);
                    //tmp = (BluetoothSocket) m.invoke(mBluetoothDevice, 1);
                }
                if (tmp != null)
                    mmSocket = tmp;
            } catch (Exception e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ClientConnectThread -> ClientConnectThread() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ClientConnectThread -> ClientConnectThread() -> :failed").sendToTarget();
                mmSocket = null;
                BluetoothChatService.this.stop();
            }
        }

        public void run() {
            try {
                setState(STATE_CONNECTING);
                mmSocket.connect();
                connected(mmSocket);
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ClientConnectThread -> run() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ClientConnectThread -> run() -> :failed").sendToTarget();
                BluetoothChatService.this.stop();
            }
        }

        public void cancel() {
            try {
                if (mmSocket != null && mmSocket.isConnected()) {
                    mmSocket.close();
                }
                mmSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ClientConnectThread -> cancel() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ClientConnectThread -> cancel() -> :failed").sendToTarget();
                mmSocket = null;
                BluetoothChatService.this.stop();
            }
        }
    }

    /**
     * 以獲取socket,建立數據流線程
     *
     * @param socket
     */
    private synchronized void connected(BluetoothSocket socket) {
        if (mReadWriteThread != null) {
            mReadWriteThread.cancel();
            mReadWriteThread = null;
        }
        mReadWriteThread = new ReadWriteThread(socket);
        mReadWriteThread.start();
    }

    /**
     * 連接成功線程,可進行讀寫操作
     */
    private class ReadWriteThread extends Thread {
        private BluetoothSocket mmSocket;
        private DataInputStream mmInStream;
        private DataOutputStream mmOutStream;
        private boolean isRunning = true;

        public ReadWriteThread(BluetoothSocket socket) {
            mmSocket = socket;
            try {
                mmInStream = new DataInputStream(mmSocket.getInputStream());
                mmOutStream = new DataOutputStream(mmSocket.getOutputStream());
                // 連接建立成功
                setState(STATE_CONNECTED);
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ReadWriteThread -> ReadWriteThread() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ReadWriteThread -> ReadWriteThread() -> :failed").sendToTarget();
                mmOutStream = null;
                mmInStream = null;
                BluetoothChatService.this.stop();
            }
        }

        public void run() {
            byte[] buffer = new byte[1024];
            int len;
            while (isRunning) {
                try {
                    //readUTF(),read(buffer) 都是阻塞式的方法
                    //如果這兒用readUTF,那麼寫的地方得用writeUTF。對應
                    String receive_str = mmInStream.readUTF();
                    if (!TextUtils.isEmpty(receive_str))
                        mHandler.obtainMessage(MESSAGE_RECEIVE, -1, -1, receive_str).sendToTarget();
//                    len = mmInStream.read(buffer);
//                    if(len > 0){
//                        String receive_str = new String(buffer,0,len);
//                        if (!TextUtils.isEmpty(receive_str))
//                            mHandler.obtainMessage(MESSAGE_RECEIVE, -1, -1, receive_str).sendToTarget();
//                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    if (DEBUG)
                        Log.e(TAG, "ReadWriteThread -> run() -> :failed " + e.getMessage());
                    mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ReadWriteThread -> run() -> :failed").sendToTarget();
                    BluetoothChatService.this.stop();
                }
            }
        }

        public void write(String str) {
            try {
                mmOutStream.writeUTF(str);
                mmOutStream.flush();
                mHandler.obtainMessage(MESSAGE_TOAST_SEND, -1, -1, str).sendToTarget();
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ReadWriteThread -> write() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ReadWriteThread -> write() -> :failed").sendToTarget();
                BluetoothChatService.this.stop();
            }
        }

        public void cancel() {
            try {
                isRunning = false;
                if (mmInStream != null) {
                    mmInStream.close();
                    mmInStream = null;
                }
                if (mmOutStream != null) {
                    mmOutStream.close();
                    mmOutStream = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (DEBUG)
                    Log.e(TAG, "ReadWriteThread -> cancel() -> :failed " + e.getMessage());
                mHandler.obtainMessage(MESSAGE_TOAST_ERROR, -1, -1, "ReadWriteThread -> cancel() -> :failed").sendToTarget();
                mmInStream = null;
                mmOutStream = null;
                BluetoothChatService.this.stop();
            }
        }
    }

sorry,不要蛋疼,不要罵娘,代碼確實有這麼多。以上代碼使我在前輩的基礎上優化改進了的。可以作為一個公共的藍牙通訊工具類使用。

 

下面貼出我的項目功能,是一個聊天程序,只能單聊。我不明太網上有很多demo聲稱能群聊怎麼實現的,據目前分析,服務器端和客戶端的管道流是一一對應的,不是廣播模式。如果能群聊,會在服務器端創建一個輸入輸出流管理的集合吧,服務器端沒收到一條消息,在=再循環輸出流集合,往各個客戶端都發送消息。這樣一來,我上面的這段代碼不夠用了。懶得改,故沒有做群聊。

先貼圖:

\

\

\

\

 

 

程序中,socket的連接方式有安全連接和不安全連接,我一直沒有搞懂區別

在兩個手機都連接了wifi的情況加,再使用我這種藍牙通訊方式通訊時,io流連接上後會自動斷開,很奇怪。查資料說藍牙通訊的波段頻率與路由器的沖突了。沒轍,故在啟動程序的時候關閉了wifi,下下策,望大家提供思路。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved