android 藍牙連接端(客戶端)封裝


0.權限  AndroidManifest.xml

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

<activity
android:name=".DeviceListActivity"
android:configChanges="screenSize|keyboardHidden|orientation"
android:launchMode="singleInstance"
android:screenOrientation="portrait"/>

1.設備列表布局  activity_device_list.xml (主要就一個listview了)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:gravity="center"
        android:text="掃描到的藍牙"/>

    <ListView
        android:id="@+id/listViewMessage"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:fastScrollEnabled="true"/>


</LinearLayout>
View Code

2.設備列表java代碼  DeviceListActivity.java

package de.bvb.bluetoothchat;

import android.app.Activity;
import android.app.Dialog;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import de.bvb.bluetoothchat.utils.BlueToothConnectCallback;
import de.bvb.bluetoothchat.utils.BluetoothDeviceInfo;
import de.bvb.bluetoothchat.utils.ClientUtil;

/**
 * Created by Administrator on 2017/06/01.
 */

public class DeviceListActivity extends Activity implements AdapterView.OnItemClickListener {
    List<String> list;
    ArrayAdapter<String> adapter;

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

        ListView listView = (ListView) findViewById(R.id.listViewMessage);


        list = new ArrayList<>();
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(this);

        ClientUtil.getInstance().onCreate(this);
        ClientUtil.getInstance().setOnFoundUnBondDeviceListener(new ClientUtil.OnFoundUnBondDeviceListener() {
            @Override
            public void foundUnBondDevice(BluetoothDevice unBondDevice) {
                list.add(unBondDevice.getName() + "|" + unBondDevice.getAddress());
                adapter.notifyDataSetChanged();
            }
        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        List<BluetoothDeviceInfo> bluetoothDeviceInfoList = ClientUtil.getInstance().scanDevice();
        list.clear();
        for (BluetoothDeviceInfo bluetoothDeviceInfo : bluetoothDeviceInfoList) {
            list.add(bluetoothDeviceInfo.toString());
            adapter.notifyDataSetChanged();
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        final Dialog dialog = new Dialog(this);
        dialog.setTitle("正在連接..");
        dialog.show();
        String macAddress = list.get(position).split("\\|")[1];//
        ClientUtil.getInstance().connectRemoteDevice(macAddress, new BlueToothConnectCallback() {
            @Override
            public void connecting(String serverBlueToothAddress) {

            }

            @Override
            public void connectSuccess(String serverBlueToothAddress) {
                dialog.dismiss();
                Toast.makeText(DeviceListActivity.this, "連接成功", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(DeviceListActivity.this, ClientActivity.class));
            }

            @Override
            public void connectFailure(IOException e) {
                dialog.dismiss();
                Toast.makeText(DeviceListActivity.this, "連接失敗..", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ClientUtil.getInstance().unregisterReceiver(this);
    }
}
View Code

3.通信頁面調用代碼(收消息,發消息)

        // 注冊收到消息以后的事件
        ClientUtil.getInstance().setOnReceivedMessageListener(new ReceivedMessageListener() {
            @Override
            public void onReceiveMessage(final String messageContent) {
                list.add(new MessageEntity(messageContent, true));
                listViewAdapterMessage.setData(list);
            }

            @Override
            public void onConnectionInterrupt(IOException e) {
                btnSend.setEnabled(false);
                etMessage.setEnabled(false);
                Toast.makeText(ClientActivity.this, "連接中斷", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(ClientActivity.this, DeviceListActivity.class));
            }
        });
        // 發送消息
        ClientUtil.getInstance().sendMessage(message);
        list.add(new MessageEntity(message, false));
        listViewAdapterMessage.setData(list);

4.工具類

package de.bvb.bluetoothchat.utils;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.text.TextUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * 客戶端(連接端)工具類
 */
public class ClientUtil {

    public static final String TAG = "BluetoothManagerUtil";

    ///////////////////////////////////////////////////////////////////////////
    // 單例模式
    private ClientUtil() { }

    public static synchronized ClientUtil getInstance() {
        return SingletonHolder.instance;
    }

    private static final class SingletonHolder {
        private static ClientUtil instance = new ClientUtil();
    }
    ///////////////////////////////////////////////////////////////////////////

    private String serverBlueToothAddress;  //連接藍牙地址
    private BluetoothSocket socket = null; // 客戶端socket
    private BluetoothAdapter bluetoothAdapter;

    /** 打開藍牙,注冊掃描藍牙的廣播 onCreate()中執行.連接頁面調用 */
    public void onCreate(Activity activity) {
        registerBluetoothScanReceiver(activity);
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (null != bluetoothAdapter) { //本地藍牙存在...
            if (!bluetoothAdapter.isEnabled()) { //判斷藍牙是否被打開...
                // 發送打開藍牙的意圖,系統會彈出一個提示對話框,打開藍牙是需要傳遞intent的...
                Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                //打開本機的藍牙功能...使用startActivityForResult()方法...這里我們開啟的這個Activity是需要它返回執行結果給主Activity的...
                activity.startActivityForResult(enableIntent, Activity.RESULT_FIRST_USER);

                Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                // 設置藍牙的可見性,最大值3600秒,默認120秒,0表示永遠可見(作為客戶端,可見性可以不設置,服務端必須要設置)
                displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
                //這里只需要開啟另一個activity,讓其一直顯示藍牙...沒必要把信息返回..因此調用startActivity()
                activity.startActivity(displayIntent);

                // 直接打開藍牙
                bluetoothAdapter.enable();//這步才是真正打開藍牙的部分....
                LogUtil.d(TAG, "打開藍牙成功");
            } else {
                LogUtil.d(TAG, "藍牙已經打開了...");
            }
        } else {
            LogUtil.d(TAG, "當前設備沒有藍牙模塊");
        }
    }


    /** 掃描設備 onResume()中執行.連接頁面調用 */
    public List<BluetoothDeviceInfo> scanDevice() {
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
            LogUtil.e(TAG, "藍牙狀態異常");
            return null;
        }
        List<BluetoothDeviceInfo> bluetoothDeviceInfoList = new ArrayList<>();
        if (bluetoothAdapter.isDiscovering()) { // 如果正在處於掃描過程...
            /** 停止掃描 */
            bluetoothAdapter.cancelDiscovery(); // 取消掃描...
        } else {
            // 每次掃描前都先判斷一下是否存在已經配對過的設備
            Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
            if (pairedDevices.size() > 0) {
                BluetoothDeviceInfo bluetoothDeviceInfo;
                for (BluetoothDevice device : pairedDevices) {
                    bluetoothDeviceInfo = new BluetoothDeviceInfo(device.getName() + "", device.getAddress() + "");
                    bluetoothDeviceInfoList.add(bluetoothDeviceInfo);
                    LogUtil.d(TAG, "已經匹配過的設備:" + bluetoothDeviceInfo.toString());
                }
            } else {
                LogUtil.d(TAG, "沒有已經配對過的設備");
            }
            /* 開始搜索 */
            bluetoothAdapter.startDiscovery();
        }
        return bluetoothDeviceInfoList;
    }

    /** 通過Mac地址去嘗試連接一個設備.連接頁面調用 */
    public void connectRemoteDevice(final String serverBlueToothAddress, BlueToothConnectCallback connectInterface) {
        this.serverBlueToothAddress = serverBlueToothAddress;
        final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(serverBlueToothAddress);
        ThreadPoolUtil.execute(new ConnectRunnable(device, connectInterface));
    }

    /** 廣播反注冊.連接頁面調用 */
    public void unregisterReceiver(Activity activity) {
        if (receiver != null && receiver.getAbortBroadcast()) {
            activity.unregisterReceiver(receiver);
        }
    }

    /** 發送消息,在通信頁面使用 */
    public void sendMessage(String message) {
        try {
            writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            writer.write(message + "\n");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /** 收到消息的監聽事件,在通信頁面注冊這個事件 */
    public void setOnReceivedMessageListener(ReceivedMessageListener listener) {
        if (listener != null) {
            // 可以開啟讀數據線程
            //     MainHandler.getInstance().post(new ReadRunnable(listener));
            ThreadPoolUtil.execute(new ReadRunnable(listener));
        }
    }

    /** 關閉藍牙,在app退出時調用 */
    public void onExit() {
        if (bluetoothAdapter != null) {
            bluetoothAdapter.cancelDiscovery();
            // 關閉藍牙
            bluetoothAdapter.disable();
        }
        closeCloseable(writer, socket);
    }

    /** 連接線程 */
    class ConnectRunnable implements Runnable {
        private BluetoothDevice device; // 藍牙設備
        private BlueToothConnectCallback connectInterface;

        public ConnectRunnable(BluetoothDevice device, BlueToothConnectCallback connectInterface) {
            this.device = device;
            this.connectInterface = connectInterface;
        }

        @Override
        public void run() {
            if (null != device) {
                try {
                    if (socket != null) {
                        closeCloseable(socket);
                    }
                    socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
                    // 連接
                    LogUtil.d(TAG, "正在連接 " + serverBlueToothAddress);
                    connectInterface.connecting(serverBlueToothAddress);
//                    Message.obtain(handler, MESSAGE_TYPE_SEND, "請稍候,正在連接服務器: " + serverBlueToothAddress).sendToTarget();

                    socket.connect();
                    MainHandler.getInstance().post(new Runnable() {
                        @Override
                        public void run() {
                            connectInterface.connectSuccess(serverBlueToothAddress);
                            LogUtil.d(TAG, "連接 " + serverBlueToothAddress + " 成功 ");
                        }
                    });
                    // 如果實現了連接,那么服務端和客戶端就共享一個RFFCOMM信道...
//                    Message.obtain(handler, MESSAGE_TYPE_SEND, "已經連接上服務端!可以發送信息").sendToTarget();
                    // 如果連接成功了...這步就會執行...更新UI界面...否則走catch(IOException e)
//                    Message.obtain(handler, MESSAGE_ID_REFRESH_UI).sendToTarget();

                    // 屏蔽點擊事件
//                    listViewMessage.setOnItemClickListener(null);
                } catch (final IOException e) {
                    MainHandler.getInstance().post(new Runnable() {
                        @Override
                        public void run() {
                            connectInterface.connectFailure(e);
                            LogUtil.d(TAG, "連接" + serverBlueToothAddress + "失敗 " + e.getMessage());
                        }
                    });
//                    e.printStackTrace();
                }
            }
        }
    }

    private BufferedWriter writer = null;

    class ReadRunnable implements Runnable {
        private ReceivedMessageListener listener;

        public ReadRunnable(ReceivedMessageListener listener) {
            this.listener = listener;
        }

        public void run() {
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String content;
                while (!TextUtils.isEmpty(content = reader.readLine())) {
                    final String finalContent = content;
                    MainHandler.getInstance().post(new Runnable() {
                        @Override
                        public void run() {
                            listener.onReceiveMessage(finalContent);
                        }
                    });
//                    Message.obtain(handler, MESSAGE_TYPE_RECEIVED, content).sendToTarget();
                }
            } catch (final IOException e) {
                MainHandler.getInstance().post(new Runnable() {
                    @Override
                    public void run() {
                        LogUtil.d(TAG, "連接中斷 " + e.getMessage());
                        listener.onConnectionInterrupt(e);
                    }
                });
                // 連接斷開
//                Message.obtain(handler, MESSAGE_ID_DISCONNECT).sendToTarget();
            }
            closeCloseable(reader);
        }
    }

    private BroadcastReceiver registerBluetoothScanReceiver(Activity activity) {
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        activity.registerReceiver(receiver, filter);
        return receiver;
    }

    public void setOnFoundUnBondDeviceListener(OnFoundUnBondDeviceListener onFoundUnBondDeviceListener) {
        this.onFoundUnBondDeviceListener = onFoundUnBondDeviceListener;
    }

    private OnFoundUnBondDeviceListener onFoundUnBondDeviceListener;

    public interface OnFoundUnBondDeviceListener {
        void foundUnBondDevice(BluetoothDevice unBondDevice);
    }

    private void closeCloseable(Closeable... closeable) {
        if (null != closeable && closeable.length > 0) {
            for (int i = 0; i < closeable.length; i++) {
                if (closeable[i] != null) {
                    try {
                        closeable[i].close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        closeable[i] = null;
                    }
                }
            }
        }
    }

    /**
     * 下面是注冊receiver監聽,注冊廣播...說一下為什么要注冊廣播...
     * 因為藍牙的通信,需要進行設備的搜索,搜索到設備后我們才能夠實現連接..如果沒有搜索,那還談什么連接...
     * 因此我們需要搜索,搜索的過程中系統會自動發出三個廣播...這三個廣播為:
     * ACTION_DISCOVERY_START:開始搜索...
     * ACTION_DISCOVERY_FINISH:搜索結束...
     * ACTION_FOUND:正在搜索...一共三個過程...因為我們需要對這三個響應過程進行接收,然后實現一些功能,因此
     * 我們需要對廣播進行注冊...知道廣播的人應該都知道,想要對廣播進行接收,必須進行注冊,否則是接收不到的...
     */
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {//正在搜索過程...
                // 通過EXTRA_DEVICE附加域來得到一個BluetoothDevice設備
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 如果這個設備是不曾配對過的,添加到list列表
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    if (null != onFoundUnBondDeviceListener) {
                        LogUtil.d(TAG, "發現沒有配對過的設備:" + parseDevice2BluetoothDeviceInfo(device));
                        onFoundUnBondDeviceListener.foundUnBondDevice(device);
                    }
                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//搜索結束后的過程...
                    LogUtil.d(TAG, "沒有發現設備");
                }
            }
        }
    };

    private String parseDevice2BluetoothDeviceInfo(BluetoothDevice device) {
        if (device == null) {
            return "device == null";
        }
        return new BluetoothDeviceInfo(device.getName(), device.getAddress()).toString();
    }
}

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM