一、添加蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
二、在文件res/values/strings.xml里,添加程序运行过程中的状态描述文本
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">蓝牙Demo</string>
<string name="send">发送</string>
<string name="not_connected">你没有链接一个设备</string>
<string name="bt_not_enabled_leaving">蓝牙不可用,离开聊天室</string>
<string name="title_connecting">链接中...</string>
<string name="title_connected_to">连接到:</string>
<string name="title_not_connected">无链接</string>
<string name="scanning">蓝牙设备搜索中...</string>
<string name="select_device">选择一个好友链接</string>
<string name="none_paired">没有配对好友</string>
<string name="none_found">附近没有发现好友</string>
<string name="title_paired_devices">已配对好友</string>
<string name="title_other_devices">其它可连接好友</string>
<string name="button_scan">搜索好友</string>
<string name="connect">我的好友</string>
<string name="discoverable">设置在线</string>
<string name="back">退出</string>
<string name="startVideo">开始聊天</string>
<string name="stopVideo">结束聊天</string>
</resources>
三、设计界面布局
<resources>
<string name="app_name">MyWeChat</string>
<string name="send">发送</string>
<string name="not_connected">你没有链接一个设备</string>
<string name="bt_not_enabled_leaving">蓝牙不可用,离开聊天室</string>
<string name="title_connecting">链接中...</string>
<string name="title_connected_to">连接到:</string>
<string name="title_not_connected">无链接</string>
<string name="scanning">蓝牙设备搜索中...</string>
<string name="select_device">选择一个好友链接</string>
<string name="none_paired">没有配对好友</string>
<string name="none_found">附近没有发现好友</string>
<string name="title_paired_devices">已配对好友</string>
<string name="title_other_devices">其它可连接好友</string>
<string name="button_scan">搜索好友</string>
<string name="connect">我的好友</string>
<string name="discoverable">设置在线</string>
<string name="back">退出</string>
<string name="startVideo">开始聊天</string>
<string name="stopVideo">结束聊天</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/title_left_text"
style="?android:attr/windowTitleStyle"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_weight="1"
android:gravity="left"
android:ellipsize="end"
android:singleLine="true" />
<TextView
android:id="@+id/title_right_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="right"
android:singleLine="true"
android:textColor="#fff" />
</LinearLayout>
</android.support.v7.widget.Toolbar>
<ListView android:id="@+id/in"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
android:layout_weight="1" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<EditText android:id="@+id/edit_text_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="bottom" />
<Button android:id="@+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send"/>
</LinearLayout>
四、编写用于蓝牙会话的服务组件ChatService
AcceptThread
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
try {
//使用射频端口(RF comm)监听
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) {
}
mmServerSocket = tmp;
}
@Override
public void run() {
setName("AcceptThread");
BluetoothSocket socket = null;
while (mState != STATE_CONNECTED) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
if (socket != null) {
synchronized (ChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
}
}
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ConnectThread
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
e.printStackTrace();
}
mmSocket = tmp;
}
@Override
public void run() {
setName("ConnectThread");
mAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
connectionFailed();
try {
mmSocket.close();
} catch (IOException e2) {
e.printStackTrace();
}
ChatService.this.start();
return;
}
synchronized (ChatService.this) {
mConnectThread = null;
}
connected(mmSocket, mmDevice);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ConnectedThread
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
@Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
connectionLost();
break;
}
}
}
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
} catch (IOException e) {
e.printStackTrace();
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
五、分别建立供主Activity使用的菜单文件res/menu/optionmenu.xml、选择好友(即已经配对过的蓝牙设备)的界面布局文件devicelist.xml
DeviceList.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/title_paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/title_paired_devices"
android:visibility="gone"
android:background="#666"
android:textColor="#fff"
android:paddingLeft="5dp" />
<ListView android:id="@+id/paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView android:id="@+id/title_new_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/title_other_devices"
android:visibility="gone"
android:background="#666"
android:textColor="#fff"
android:paddingLeft="5dp" />
<!--android:visibility="gone"表示不占空间的隐藏,invisible是占空间-->
<ListView android:id="@+id/new_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2" />
<Button android:id="@+id/button_scan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button_scan" />
</LinearLayout>
六、新建Activity组件DeviceList,实现选取与之会话的蓝牙设备
本程序供菜单项主界面的选项菜单“我的友好”调用,用于: (1)显示已配对的好友列表; (2)搜索可配对的好友进行配对 (3)新选择并配对的蓝牙设备将刷新好友列表
关键技术:动态注册一个广播接收者,处理蓝牙设备扫描的结果
其中两个监听事件mPaireDeviceClickListener,mNewDeviceClickListener为
private AdapterView.OnItemClickListener mPaireDeviceClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
mBtAdapter.cancelDiscovery();
String info = ((TextView) v).getText().toString();
String address = info.substring(info.length() - 17);
Intent intent = new Intent();
intent.putExtra(EXTRA_DEVICE_ADDRESS, address); //Mac地址
setResult(Activity.RESULT_OK, intent);
finish();
}
};
private AdapterView.OnItemClickListener mNewDeviceClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
mBtAdapter.cancelDiscovery();
Toast.makeText(DeviceList.this, "请在蓝牙设置界面手动连接设备", Toast.LENGTH_SHORT).show();
Intent intent = new Intent( Settings.ACTION_BLUETOOTH_SETTINGS);
startActivityForResult(intent,1);
}
};
七、定义已配对和未配对的蓝牙设备列表
八、使用菜单File→Refactor→Rename,重命名模块的MainActivity为BluetoothChat
九、附加device_name和message两个xml文件
添加一个TextView组件即可
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
/>
十、源码参考及学习链接
https://developer.android.google.cn/guide/topics/connectivity/bluetooth
http://www.android-doc.com/guide/topics/connectivity/bluetooth.html
https://www.jianshu.com/p/8fbbc6723a7c
https://blog.csdn.net/weixin_39079048/article/details/78923669?
BlueToothChat
GitHub - HS-98/MyWeChat
|