最近有需求,要开发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