最近有需求,要开发APP之间直接分享一点数据,想了想蓝牙传输是最为合适的。于是就学了一下蓝牙的传输。

蓝牙(BlueTooth)是一种短距离的无线通信技术标准。

蓝牙协议分为四层,即核心协议层、电缆替代协议层、电话控制协议层和采纳的其他协议层。这四种协议中最重要的是核心协议。

蓝牙的核心协议包括系带、链路管理、逻辑链路控制和适应协议四部分组成。其中链路管理(LMP)负责蓝牙组件间连接的建立。逻辑链路控制与适应协议(L2CAP)位于基带协议层上,属于数据链路层,是一个为高层传输和应用层协议屏蔽基带协议的适配协议。

第一种打开蓝牙的方式:

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);

第二种打开蓝牙的方式,必须设置权限:

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

然后代码实现:(不建议使用这种,有的手机不行)

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        mBluetoothAdapter.enable();
        mBluetoothAdapter.disable();

通过蓝牙传输数据与Socket类似。在网络中使用Socket和ServerSocket控制客户端和服务端的数据读写。而蓝牙通讯也由客户端和服务端Socket来完成。蓝牙客户端Socket是BluetoothSocket,蓝牙服务端Socket是BluetoothServerSocket。这两个雷都在android.bluetooth包中。

无论是BluetoothSocket,还是BluetoothServerSocket,都需要一个UUID(全局唯一标识符,Universally Unique Identifier)。格式如下:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

UUID的格式被分成5端,其中中间3段的字符数相同,都是4,第1段是8个字符,最后一段是12个字符。所以UUID实际上是一个8-4-4-4-12的字符串。

UUID相当于Socket的端口,而蓝牙地址相当于Socket的IP。

接下来代码演示,也不难,所以就全部贴出来:

public class MainActivity extends ActionBarActivity implements OnClickListener,
        OnItemClickListener {
    private static final int REQUEST_ENABLE_BT = 1;
    private final UUID MY_UUID = UUID
            .fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");
    private final String NAME = "Bluetooth_Socket";

    private Button btnScan;
    private Button btnOpen;
    private ListView lvDevices;


    private BluetoothAdapter bluetoothAdapter;

    private List<String> bluetoothDevices = new ArrayList<String>();

    private ArrayAdapter<String> arrayAdapter;


    private BluetoothSocket clientSocket;
    private BluetoothDevice device;



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

        btnScan = (Button) findViewById(R.id.btnScan);
        btnOpen = (Button) findViewById(R.id.btnOpen);
        lvDevices = (ListView) findViewById(R.id.lvDevices);

        btnScan.setOnClickListener(this);
        btnOpen.setOnClickListener(this);

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
        if (devices.size() > 0) {
            for (BluetoothDevice device : devices) {
                bluetoothDevices.add(device.getName() + ":"
                        + device.getAddress() + "\n");
            }
        }

        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        this.registerReceiver(receiver, filter);

        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        this.registerReceiver(receiver, filter);

        //适配List列表
        arrayAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1,
                bluetoothDevices);

        lvDevices.setAdapter(arrayAdapter);
        lvDevices.setOnItemClickListener(this);

        //开启蓝牙服务端的线程
        AcceptThread acceptThread = new AcceptThread();
        acceptThread.start();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btnScan:
            //扫描蓝牙
            setProgressBarIndeterminateVisibility(true);
            setTitle("正在扫描...");
            if (bluetoothAdapter.isDiscovering()) {
                bluetoothAdapter.cancelDiscovery();
            }
            bluetoothAdapter.startDiscovery();
            break;

        case R.id.btnOpen:
            // 打开蓝牙
            if (!bluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(
                        BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            }

            break;
        }

    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        String s = arrayAdapter.getItem(position);
        String address = s.substring(s.indexOf(":") + 1).trim();
        OutputStream out = null;
        try {
            if (bluetoothAdapter.isDiscovering()) {
                bluetoothAdapter.cancelDiscovery();
            }
            try {
                if (device == null) {
                    device = bluetoothAdapter.getRemoteDevice(address);
                }
                if (clientSocket == null) {
                    clientSocket = device
                            .createRfcommSocketToServiceRecord(MY_UUID);
                    clientSocket.connect();
                    out = clientSocket.getOutputStream();
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if (out != null) {
                // 发送数据的定法
                out.write("LayneYao".getBytes());

            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 扫描其他蓝牙的广播
     */
    private final BroadcastReceiver receiver = 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);
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    bluetoothDevices.add(device.getName() + ":"
                            + device.getAddress() + "\n");
                    arrayAdapter.notifyDataSetChanged();
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
                    .equals(action)) {
                setProgressBarIndeterminate(false);
                setTitle("已搜索完");
            }

        }

    };

    /**
     * 显示服务端接收的信息
     */
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Toast.makeText(MainActivity.this, String.valueOf(msg.obj),
                    Toast.LENGTH_SHORT).show();

        }
    };

    /**
     * 蓝牙服务端的线程
     */
    private class AcceptThread extends Thread {
        private BluetoothServerSocket serverSocket;
        private BluetoothSocket socket;
        private InputStream in;
        private OutputStream out;

        public AcceptThread() {
            try {
                serverSocket = bluetoothAdapter
                        .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void run() {

            try {
                socket = serverSocket.accept();
                in = socket.getInputStream();
                out = socket.getOutputStream();

                while (true) {
                    byte[] buffer = new byte[10];
                    in.read(buffer);

                    Message message = Message.obtain();
                    message.obj = new String(buffer);
                    handler.sendMessage(message);
                }

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                try {
                    if (in != null) {
                        in.close();
                    }

                    if (out != null) {
                        out.close();
                    }

                    if (socket != null) {
                        socket.close();
                    }

                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        }
    }

}

布局文件代码:

<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.itman.bluetoothsocket.MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btnOpen"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="打开蓝牙" />

        <Button
            android:id="@+id/btnScan"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="扫描蓝牙" />
    </LinearLayout>

    <ListView
        android:id="@+id/lvDevices"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

</LinearLayout>

最后,添加以下权限:

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

好了运行到两部真机,只要找到对应的蓝牙名称,点击即可尝试连接并传输数据。亲测过可行,只是用户体验不高,操作有点复杂,这里就不演示了。

两个蓝牙设备进行连接是需要使用同一个UUID。但是你细心会发现,有很多型号的手机(可能是非Android系统的手机)之间使用了不同的程序也可以使用蓝牙进行通讯,从表面上看,它们之间几乎不可能使用同一个UUID。

实际上UUID和TCP的端口一样,也有一些默认的值。例如,将蓝牙模拟成串口的服务就使用了一个标准的UUID:

00001101-0000-1000-8000-00805F9B34FB。

除此之外,还有很多标准的UUID如下面就是两个标准的UUID:

  • 信息同步服务:00001104-0000-1000-8000-00805F9B34FB
  • 文件传输服务:00001106-0000-1000-8000-00805F9B34FB