当前位置:   article > 正文

Android Studio 入门:(七) 安卓蓝牙聊天功能的实现_android studio实现蓝牙聊天

android studio实现蓝牙聊天

目录

1. 清单文件注册权限

2. 在文件res/values/strings.xml里,添加程序运行过程中的状态描述文本及配色代码等

3.修改聊天界面布局文件

4.用于蓝牙会话的服务组件ChatService

5.建立选择蓝牙设备的布局文件 device_list.xml

6.新建Activity组件DeviceList,实现选取与之会话的蓝牙设备

7. 界面程序weixinFragment.java

8.源码地址


1. 清单文件注册权限

  1. <!--下面2个是普通权限,只需要在清单文件里注册,不需要在程序里动态申请-->
  2. <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
  3. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  4. <!--搜索WiFi,需要定位权限,它是危险权限,在此声明外,还需要在程序里动态申请-->
  5. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

2. 在文件res/values/strings.xml里,添加程序运行过程中的状态描述文本及配色代码等

  1. <resources>
  2. <string name="app_name">BlueTooth_Chat</string>
  3. <string name="top_name">蓝牙聊天</string>
  4. <string name="bottom_image">底部图标</string>
  5. <string name="bottom_name1">微信</string>
  6. <string name="bottom_name2">朋友</string>
  7. <string name="bottom_name3">通讯录</string>
  8. <string name="bottom_name4">设置</string>
  9. <string name="send">发送</string>
  10. <string name="not_connected">你没有链接一个设备</string>
  11. <string name="bt_not_enabled_leaving">蓝牙不可用,离开聊天室</string>
  12. <string name="title_connecting">链接中...</string>
  13. <string name="title_connected_to">连接到:</string>
  14. <string name="title_not_connected">无链接</string>
  15. <string name="scanning">蓝牙设备搜索中...</string>
  16. <string name="select_device">选择一个好友链接</string>
  17. <string name="none_paired">没有配对好友</string>
  18. <string name="none_found">附近没有发现好友</string>
  19. <string name="title_paired_devices">已配对好友</string>
  20. <string name="title_other_devices">其它可连接好友</string>
  21. <string name="button_scan">搜索好友</string>
  22. <string name="connect">我的好友</string>
  23. <string name="discoverable">设置在线</string>
  24. <string name="back">退出</string>
  25. <string name="startVideo">开始聊天</string>
  26. <string name="stopVideo">结束聊天</string>
  27. <!-- TODO: Remove or change this placeholder text -->
  28. <string name="hello_blank_fragment">Hello blank fragment</string>
  29. </resources>

3.修改聊天界面布局文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/device_name"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:background="@color/bg01"
  7. android:orientation="vertical">
  8. <!--新版Android支持的Toolbar,对标题栏布局-->
  9. <!-- <android.support.v7.widget.Toolbar-->
  10. <androidx.appcompat.widget.Toolbar
  11. android:id="@+id/toolbar"
  12. android:layout_width="match_parent"
  13. android:layout_height="34dp">
  14. <LinearLayout
  15. android:layout_width="match_parent"
  16. android:layout_height="match_parent"
  17. android:orientation="horizontal">
  18. <TextView
  19. android:id="@+id/title_left_text"
  20. style="?android:attr/windowTitleStyle"
  21. android:layout_width="0dp"
  22. android:layout_height="match_parent"
  23. android:layout_alignParentLeft="true"
  24. android:layout_weight="1"
  25. android:ellipsize="end"
  26. android:gravity="left"
  27. android:singleLine="true" />
  28. <TextView
  29. android:id="@+id/title_right_text"
  30. android:layout_width="0dp"
  31. android:layout_height="match_parent"
  32. android:layout_alignParentRight="true"
  33. android:layout_weight="1"
  34. android:ellipsize="end"
  35. android:gravity="right"
  36. android:singleLine="true"
  37. android:textColor="#fff" />
  38. </LinearLayout>
  39. </androidx.appcompat.widget.Toolbar>
  40. <!-- </android.support.v7.widget.Toolbar>-->
  41. <ListView
  42. android:id="@+id/in"
  43. android:layout_width="match_parent"
  44. android:layout_height="match_parent"
  45. android:layout_weight="1"
  46. android:stackFromBottom="true"
  47. android:transcriptMode="alwaysScroll" />
  48. <LinearLayout
  49. android:layout_width="match_parent"
  50. android:layout_height="wrap_content"
  51. android:orientation="horizontal">
  52. <EditText
  53. android:id="@+id/edit_text_out"
  54. android:layout_width="0dp"
  55. android:layout_height="wrap_content"
  56. android:layout_gravity="bottom"
  57. android:layout_weight="1" />
  58. <Button
  59. android:id="@+id/button_send"
  60. android:layout_width="wrap_content"
  61. android:layout_height="wrap_content"
  62. android:text="@string/send" />
  63. </LinearLayout>
  64. </LinearLayout>

4.用于蓝牙会话的服务组件ChatService

  1. package com.e.bluetooth_chat;
  2. import android.bluetooth.BluetoothAdapter;
  3. import android.bluetooth.BluetoothDevice;
  4. import android.bluetooth.BluetoothServerSocket;
  5. import android.bluetooth.BluetoothSocket;
  6. import android.content.Context;
  7. import android.os.Bundle;
  8. import android.os.Handler;
  9. import android.os.Message;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.io.OutputStream;
  13. import java.util.UUID;
  14. public class ChatService {
  15. //本应用的主Activity组件名称
  16. private static final String NAME = "BlueToothChat";
  17. // UUID:通用唯一识别码,是一个128位长的数字,一般用十六进制表示
  18. //算法的核心思想是结合机器的网卡、当地时间、一个随机数来生成
  19. //在创建蓝牙连接
  20. private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
  21. private final BluetoothAdapter mAdapter;
  22. private final Handler mHandler;
  23. private AcceptThread mAcceptThread;
  24. private ConnectThread mConnectThread;
  25. private ConnectedThread mConnectedThread;
  26. private int mState;
  27. public static final int STATE_NONE = 0;
  28. public static final int STATE_LISTEN = 1;
  29. public static final int STATE_CONNECTING = 2;
  30. public static final int STATE_CONNECTED = 3;
  31. //构造方法,接收UI主线程传递的对象
  32. public ChatService(Context context,Handler handler){
  33. //构造方法完成蓝牙对象的创建
  34. mAdapter = BluetoothAdapter.getDefaultAdapter();
  35. mState = STATE_NONE;
  36. mHandler = handler;
  37. }
  38. private synchronized void setState(int state){
  39. mState = state;
  40. mHandler.obtainMessage(weixinFragment.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
  41. }
  42. public synchronized int getState(){
  43. return mState;
  44. }
  45. public synchronized void start(){
  46. if (mConnectThread != null) {
  47. mConnectThread.cancel();
  48. mConnectThread = null;
  49. }
  50. if (mConnectedThread != null) {
  51. mConnectedThread.cancel();
  52. mConnectedThread = null;
  53. }
  54. if (mAcceptThread == null) {
  55. mAcceptThread = new AcceptThread();
  56. mAcceptThread.start();
  57. }
  58. setState(STATE_LISTEN);
  59. }
  60. //取消 CONNECTING 和 CONNECTED 状态下的相关线程,然后运行新的 mConnectThread 线程
  61. public synchronized void connect(BluetoothDevice device) {
  62. if (mState == STATE_CONNECTING) {
  63. if (mConnectThread != null) {
  64. mConnectThread.cancel();
  65. mConnectThread = null;
  66. }
  67. }
  68. if (mConnectedThread != null) {
  69. mConnectedThread.cancel();
  70. mConnectedThread = null;
  71. }
  72. mConnectThread = new ConnectThread(device);
  73. mConnectThread.start();
  74. setState(STATE_CONNECTING);
  75. }
  76. /*
  77. 开启一个 ConnectedThread 来管理对应的当前连接。之前先取消任意现存的 mConnectThread 、
  78. mConnectedThread 、 mAcceptThread 线程,然后开启新 mConnectedThread ,传入当前刚刚接受的
  79. socket 连接。最后通过 Handler来通知UI连接
  80. */
  81. public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
  82. if (mConnectThread != null) {
  83. mConnectThread.cancel();
  84. mConnectThread = null;
  85. }
  86. if (mConnectedThread != null) {
  87. mConnectedThread.cancel();
  88. mConnectedThread = null;
  89. }
  90. if (mAcceptThread != null) {
  91. mAcceptThread.cancel();
  92. mAcceptThread = null;
  93. }
  94. mConnectedThread = new ConnectedThread(socket);
  95. mConnectedThread.start();
  96. Message msg = mHandler.obtainMessage(weixinFragment.MESSAGE_DEVICE_NAME);
  97. Bundle bundle = new Bundle();
  98. bundle.putString(weixinFragment.DEVICE_NAME, device.getName());
  99. msg.setData(bundle);
  100. mHandler.sendMessage(msg);
  101. setState(STATE_CONNECTED);
  102. }
  103. // 停止所有相关线程,设当前状态为 NONE
  104. public synchronized void stop() {
  105. if (mConnectThread != null) {
  106. mConnectThread.cancel();
  107. mConnectThread = null;
  108. }
  109. if (mConnectedThread != null) {
  110. mConnectedThread.cancel();
  111. mConnectedThread = null;
  112. }
  113. if (mAcceptThread != null) {
  114. mAcceptThread.cancel();
  115. mAcceptThread = null;
  116. }
  117. setState(STATE_NONE);
  118. }
  119. // 在 STATE_CONNECTED 状态下,调用 mConnectedThread 里的 write 方法,写入 byte
  120. public void write(byte[] out) {
  121. ConnectedThread r;
  122. synchronized (this) {
  123. if (mState != STATE_CONNECTED)
  124. return;
  125. r = mConnectedThread;
  126. }
  127. r.write(out);
  128. }
  129. // 连接失败的时候处理,通知 ui ,并设为 STATE_LISTEN 状态
  130. public void connectionFailed() {
  131. setState(STATE_LISTEN);
  132. Message msg = mHandler.obtainMessage(weixinFragment.MESSAGE_TOAST);
  133. Bundle bundle = new Bundle();
  134. bundle.putString(weixinFragment.TOAST, "链接不到设备");
  135. msg.setData(bundle);
  136. mHandler.sendMessage(msg);
  137. }
  138. // 当连接失去的时候,设为 STATE_LISTEN 状态并通知 ui
  139. public void connectionLost() {
  140. setState(STATE_LISTEN);
  141. Message msg = mHandler.obtainMessage(weixinFragment.MESSAGE_TOAST);
  142. Bundle bundle = new Bundle();
  143. bundle.putString(weixinFragment.TOAST, "设备链接中断");
  144. msg.setData(bundle);
  145. mHandler.sendMessage(msg);
  146. }
  147. private class AcceptThread extends Thread {
  148. private final BluetoothServerSocket mmServerSocket;
  149. public AcceptThread() {
  150. BluetoothServerSocket tmp = null;
  151. try {
  152. //使用射频端口(RF comm)监听
  153. tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
  154. } catch (IOException e) {
  155. }
  156. mmServerSocket = tmp;
  157. }
  158. @Override
  159. public void run() {
  160. setName("AcceptThread");
  161. BluetoothSocket socket = null;
  162. while (mState != STATE_CONNECTED) {
  163. try {
  164. socket = mmServerSocket.accept();
  165. } catch (IOException e) {
  166. e.printStackTrace();
  167. break;
  168. }
  169. if (socket != null) {
  170. synchronized (ChatService.this) {
  171. switch (mState) {
  172. case STATE_LISTEN:
  173. case STATE_CONNECTING:
  174. connected(socket, socket.getRemoteDevice());
  175. break;
  176. case STATE_NONE:
  177. case STATE_CONNECTED:
  178. try {
  179. socket.close();
  180. } catch (IOException e) {
  181. e.printStackTrace();
  182. }
  183. break;
  184. }
  185. }
  186. }
  187. }
  188. }
  189. public void cancel() {
  190. try {
  191. mmServerSocket.close();
  192. } catch (IOException e) {
  193. e.printStackTrace();
  194. }
  195. }
  196. }
  197. /*
  198. 连接线程,专门用来对外发出连接对方蓝牙的请求和处理流程。
  199. 构造函数里通过 BluetoothDevice.createRfcommSocketToServiceRecord() ,
  200. 从待连接的 device 产生 BluetoothSocket. 然后在 run 方法中 connect ,
  201. 成功后调用 BluetoothChatSevice 的 connected() 方法。定义 cancel() 在关闭线程时能够关闭相关socket 。
  202. */
  203. private class ConnectThread extends Thread{
  204. private final BluetoothSocket mmSocket;
  205. private final BluetoothDevice mmDevice;
  206. public ConnectThread(BluetoothDevice device) {
  207. mmDevice = device;
  208. BluetoothSocket tmp = null;
  209. try {
  210. tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
  211. } catch (IOException e) {
  212. e.printStackTrace();
  213. }
  214. mmSocket = tmp;
  215. }
  216. @Override
  217. public void run() {
  218. setName("ConnectThread");
  219. mAdapter.cancelDiscovery();
  220. try {
  221. mmSocket.connect();
  222. } catch (IOException e) {
  223. connectionFailed();
  224. try {
  225. mmSocket.close();
  226. } catch (IOException e2) {
  227. e.printStackTrace();
  228. }
  229. ChatService.this.start();
  230. return;
  231. }
  232. synchronized (ChatService.this) {
  233. mConnectThread = null;
  234. }
  235. connected(mmSocket, mmDevice);
  236. }
  237. public void cancel() {
  238. try {
  239. mmSocket.close();
  240. } catch (IOException e) {
  241. e.printStackTrace();
  242. }
  243. }
  244. }
  245. /*
  246. 双方蓝牙连接后一直运行的线程;构造函数中设置输入输出流。
  247. run()方法中使用阻塞模式的 InputStream.read()循环读取输入流,然后发送到 UI 线程中更新聊天消息。
  248. 本线程也提供了 write() 将聊天消息写入输出流传输至对方,传输成功后回写入 UI 线程。最后使用cancel()关闭连接的 socket
  249. */
  250. private class ConnectedThread extends Thread{
  251. private final BluetoothSocket mmSocket;
  252. private final InputStream mmInStream;
  253. private final OutputStream mmOutStream;
  254. public ConnectedThread(BluetoothSocket socket) {
  255. mmSocket = socket;
  256. InputStream tmpIn = null;
  257. OutputStream tmpOut = null;
  258. try {
  259. tmpIn = socket.getInputStream();
  260. tmpOut = socket.getOutputStream();
  261. } catch (IOException e) {
  262. e.printStackTrace();
  263. }
  264. mmInStream = tmpIn;
  265. mmOutStream = tmpOut;
  266. }
  267. @Override
  268. public void run() {
  269. byte[] buffer = new byte[1024];
  270. int bytes;
  271. while (true) {
  272. try {
  273. bytes = mmInStream.read(buffer);
  274. mHandler.obtainMessage(weixinFragment.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
  275. } catch (IOException e) {
  276. connectionLost();
  277. break;
  278. }
  279. }
  280. }
  281. public void write(byte[] buffer) {
  282. try {
  283. mmOutStream.write(buffer);
  284. mHandler.obtainMessage(weixinFragment.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
  285. } catch (IOException e) {
  286. e.printStackTrace();
  287. }
  288. }
  289. public void cancel() {
  290. try {
  291. mmSocket.close();
  292. } catch (IOException e) {
  293. e.printStackTrace();
  294. }
  295. }
  296. }
  297. }

5.建立选择蓝牙设备的布局文件 device_list.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent">
  6. <TextView android:id="@+id/title_paired_devices"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:text="@string/title_paired_devices"
  10. android:visibility="gone"
  11. android:background="#666"
  12. android:textColor="#fff"
  13. android:paddingLeft="5dp"
  14. android:paddingStart="5dp" />
  15. <ListView android:id="@+id/paired_devices"
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:layout_weight="1" />
  19. <TextView android:id="@+id/title_new_devices"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content"
  22. android:text="@string/title_other_devices"
  23. android:visibility="gone"
  24. android:background="#666"
  25. android:textColor="#fff"
  26. android:paddingLeft="5dp"
  27. android:paddingStart="5dp" />
  28. <!--android:visibility="gone"表示不占空间的隐藏,invisible是占空间-->
  29. <ListView android:id="@+id/new_devices"
  30. android:layout_width="match_parent"
  31. android:layout_height="wrap_content"
  32. android:layout_weight="2" />
  33. <Button android:id="@+id/button_scan"
  34. android:layout_width="match_parent"
  35. android:layout_height="wrap_content"
  36. android:text="@string/button_scan" />
  37. </LinearLayout>

6.新建Activity组件DeviceList,实现选取与之会话的蓝牙设备

  1. package com.e.bluetooth_chat;
  2. import android.app.Activity;
  3. import android.bluetooth.BluetoothAdapter;
  4. import android.bluetooth.BluetoothDevice;
  5. import android.content.BroadcastReceiver;
  6. import android.content.Context;
  7. import android.content.Intent;
  8. import android.content.IntentFilter;
  9. import android.os.Bundle;
  10. import android.provider.Settings;
  11. import android.view.View;
  12. import android.widget.AdapterView;
  13. import android.widget.ArrayAdapter;
  14. import android.widget.Button;
  15. import android.widget.ListView;
  16. import android.widget.TextView;
  17. import android.widget.Toast;
  18. import androidx.annotation.Nullable;
  19. import androidx.appcompat.app.AppCompatActivity;
  20. import java.util.Set;
  21. /*
  22. 本程序供菜单项主界面的选项菜单“我的友好”调用,用于:
  23. 1)显示已配对的好友列表;
  24. 2)搜索可配对的好友进行配对
  25. 3)新选择并配对的蓝牙设备将刷新好友列表
  26. 注意:发现新的蓝牙设备并请求配对时,需要对应接受
  27. 关键技术:动态注册一个广播接收者,处理蓝牙设备扫描的结果
  28. */
  29. public class DeviceList extends AppCompatActivity {
  30. private BluetoothAdapter mBtAdapter;
  31. private ArrayAdapter<String> mPairedDevicesArrayAdapter;
  32. private ArrayAdapter<String> mNewDevicesArrayAdapter;
  33. public static String EXTRA_DEVICE_ADDRESS = "device_address"; //Mac地址
  34. //定义广播接收者,用于处理扫描蓝牙设备后的结果
  35. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  36. @Override
  37. public void onReceive(Context context, Intent intent) {
  38. String action = intent.getAction();
  39. if(BluetoothDevice.ACTION_FOUND.equals(action)){
  40. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  41. if(device.getBondState()!=BluetoothDevice.BOND_BONDED){
  42. mNewDevicesArrayAdapter.add(device.getName()+"\n"+device.getAddress());
  43. }
  44. }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
  45. if(mNewDevicesArrayAdapter.getCount() == 0){
  46. String noDevices = getResources().getText(R.string.none_found).toString();
  47. mNewDevicesArrayAdapter.add(noDevices);
  48. }
  49. }
  50. }
  51. };
  52. @Override
  53. protected void onCreate(@Nullable Bundle savedInstanceState) {
  54. super.onCreate(savedInstanceState);
  55. setContentView(R.layout.device_list);
  56. //在被调用活动里,设置返回结果码
  57. setResult(Activity.RESULT_CANCELED);
  58. init(); //活动界面
  59. }
  60. private void init() {
  61. Button scanButton = findViewById(R.id.button_scan);
  62. scanButton.setOnClickListener(new View.OnClickListener() {
  63. @Override
  64. public void onClick(View v) {
  65. Toast.makeText(DeviceList.this,R.string.scanning,Toast.LENGTH_LONG).show();
  66. doDiscovery(); //搜索蓝牙设备
  67. }
  68. });
  69. mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this,R.layout.device_name);
  70. mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
  71. // mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this,R.layout.device_name);
  72. // mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
  73. //已配对蓝牙设备列表
  74. ListView pairedListView =findViewById(R.id.paired_devices);
  75. pairedListView.setAdapter(mPairedDevicesArrayAdapter);
  76. pairedListView.setOnItemClickListener(mPaireDeviceClickListener);
  77. //未配对蓝牙设备列表
  78. ListView newDevicesListView = findViewById(R.id.new_devices);
  79. newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
  80. newDevicesListView.setOnItemClickListener(mNewDeviceClickListener);
  81. //动态注册广播接收者
  82. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  83. registerReceiver(mReceiver, filter);
  84. filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  85. registerReceiver(mReceiver, filter);
  86. mBtAdapter = BluetoothAdapter.getDefaultAdapter();
  87. Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
  88. if (pairedDevices.size() > 0) {
  89. findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
  90. for (BluetoothDevice device : pairedDevices) {
  91. mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
  92. }
  93. } else {
  94. String noDevices;
  95. noDevices = getResources().getText(R.string.none_paired).toString();
  96. mPairedDevicesArrayAdapter.add(noDevices);
  97. }
  98. }
  99. @Override
  100. protected void onDestroy() {
  101. super.onDestroy();
  102. if(mBtAdapter!=null){
  103. mBtAdapter.cancelDiscovery();
  104. }
  105. this.unregisterReceiver(mReceiver);
  106. }
  107. private void doDiscovery() {
  108. findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
  109. if (mBtAdapter.isDiscovering()) {
  110. mBtAdapter.cancelDiscovery();
  111. }
  112. mBtAdapter.startDiscovery(); //开始搜索蓝牙设备并产生广播
  113. //startDiscovery是一个异步方法
  114. //找到一个设备时就发送一个BluetoothDevice.ACTION_FOUND的广播
  115. }
  116. private AdapterView.OnItemClickListener mPaireDeviceClickListener = new AdapterView.OnItemClickListener() {
  117. public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
  118. mBtAdapter.cancelDiscovery();
  119. String info = ((TextView) v).getText().toString();
  120. String address = info.substring(info.length() - 17);
  121. Intent intent = new Intent();
  122. intent.putExtra(EXTRA_DEVICE_ADDRESS, address); //Mac地址
  123. setResult(Activity.RESULT_OK, intent);
  124. finish();
  125. }
  126. };
  127. private AdapterView.OnItemClickListener mNewDeviceClickListener = new AdapterView.OnItemClickListener() {
  128. public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
  129. mBtAdapter.cancelDiscovery();
  130. Toast.makeText(DeviceList.this, "请在蓝牙设置界面手动连接设备",Toast.LENGTH_SHORT).show();
  131. Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
  132. startActivityForResult(intent,1);
  133. }
  134. };
  135. @Override
  136. protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
  137. super.onActivityResult(requestCode, resultCode, data);
  138. init();
  139. }
  140. }

7. 界面程序weixinFragment.java

  1. package com.e.bluetooth_chat;
  2. import android.Manifest;
  3. import android.app.Activity;
  4. import android.bluetooth.BluetoothAdapter;
  5. import android.bluetooth.BluetoothDevice;
  6. import android.content.Intent;
  7. import android.content.pm.PackageManager;
  8. import android.os.Build;
  9. import android.os.Bundle;
  10. import androidx.annotation.NonNull;
  11. import androidx.annotation.RequiresApi;
  12. import androidx.core.app.ActivityCompat;
  13. import androidx.core.content.ContextCompat;
  14. import androidx.fragment.app.Fragment;
  15. import android.os.Handler;
  16. import android.os.Message;
  17. import android.view.KeyEvent;
  18. import android.view.LayoutInflater;
  19. import android.view.MenuItem;
  20. import android.view.View;
  21. import android.view.ViewGroup;
  22. import android.view.inputmethod.EditorInfo;
  23. import android.widget.ArrayAdapter;
  24. import android.widget.Button;
  25. import android.widget.EditText;
  26. import android.widget.ListView;
  27. import android.widget.TextView;
  28. import android.widget.Toast;
  29. import androidx.appcompat.widget.Toolbar;
  30. /**
  31. * A simple {@link Fragment} subclass.
  32. */
  33. public class weixinFragment extends Fragment {
  34. // 蓝牙通信
  35. public static final int MESSAGE_STATE_CHANGE = 1;
  36. public static final int MESSAGE_READ = 2;
  37. public static final int MESSAGE_WRITE = 3;
  38. public static final int MESSAGE_DEVICE_NAME = 4;
  39. public static final int MESSAGE_TOAST = 5;
  40. public static final String DEVICE_NAME = "device_name";
  41. public static final String TOAST = "toast";
  42. private static final int REQUEST_CONNECT_DEVICE = 1; //请求连接设备
  43. private static final int REQUEST_ENABLE_BT = 2;
  44. private TextView mTitle;
  45. private ListView mConversationView;
  46. private EditText mOutEditText;
  47. private Button mSendButton;
  48. private String mConnectedDeviceName = null;
  49. private ArrayAdapter<String> mConversationArrayAdapter;
  50. private StringBuffer mOutStringBuffer;
  51. private BluetoothAdapter mBluetoothAdapter = null;
  52. private ChatService mChatService = null;
  53. View view;
  54. @Override
  55. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  56. Bundle savedInstanceState) {
  57. view = inflater.inflate(R.layout.tab01, container, false);
  58. Toolbar toolbar = view.findViewById(R.id.toolbar);
  59. setHasOptionsMenu(true);
  60. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  61. if (ContextCompat.checkSelfPermission(view.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
  62. ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
  63. }
  64. }
  65. //创建选项菜单
  66. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  67. toolbar.inflateMenu(R.menu.option_menu);
  68. }
  69. //选项菜单监听
  70. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  71. toolbar.setOnMenuItemClickListener(new MyMenuItemClickListener());
  72. }
  73. mTitle = view.findViewById(R.id.title_left_text);
  74. mTitle.setText("蓝牙通信");
  75. mTitle = view.findViewById(R.id.title_right_text);
  76. // 得到本地蓝牙适配器
  77. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  78. if (mBluetoothAdapter == null) {
  79. Toast.makeText(view.getContext(), "蓝牙不可用", Toast.LENGTH_LONG).show();
  80. getActivity().finish();
  81. return view;
  82. }
  83. if (!mBluetoothAdapter.isEnabled()) { //若当前设备蓝牙功能未开启
  84. Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  85. startActivityForResult(enableIntent, REQUEST_ENABLE_BT); //
  86. } else {
  87. if (mChatService == null) {
  88. setupChat(); //创建会话
  89. }
  90. }
  91. return view;
  92. }
  93. @Override
  94. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  95. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  96. if(grantResults.length>0){
  97. if(grantResults[0]!=PackageManager.PERMISSION_GRANTED){
  98. Toast.makeText(view.getContext(), "未授权,蓝牙搜索功能将不可用!", Toast.LENGTH_SHORT).show();
  99. }
  100. }
  101. }
  102. private void setupChat() {
  103. mConversationArrayAdapter = new ArrayAdapter<String>(view.getContext(), R.layout.message);
  104. mConversationView = view.findViewById(R.id.in);
  105. mConversationView.setAdapter(mConversationArrayAdapter);
  106. mOutEditText = view.findViewById(R.id.edit_text_out);
  107. mOutEditText.setOnEditorActionListener(mWriteListener);
  108. mSendButton = view.findViewById(R.id.button_send);
  109. mSendButton.setOnClickListener(new View.OnClickListener() {
  110. public void onClick(View v) {
  111. TextView textView = view.findViewById(R.id.edit_text_out);
  112. String message = textView.getText().toString();
  113. sendMessage(message);
  114. }
  115. });
  116. //创建服务对象
  117. mChatService = new ChatService(view.getContext(), mHandler);
  118. mOutStringBuffer = new StringBuffer("");
  119. }
  120. private void sendMessage(String message){
  121. if(mChatService.getState() != ChatService.STATE_CONNECTED){
  122. Toast.makeText(view.getContext(),R.string.not_connected,Toast.LENGTH_LONG).show();
  123. return;
  124. }
  125. if(message.length()>0){
  126. byte[] send = message.getBytes();
  127. mChatService.write(send);
  128. mOutStringBuffer.setLength(0);
  129. mOutEditText.setText(mOutStringBuffer);
  130. }
  131. }
  132. private void ensureDiscoverable() {
  133. if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
  134. Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
  135. discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
  136. startActivity(discoverableIntent);
  137. Toast.makeText(view.getContext(), "已经设置本机蓝牙设备的可见性,对方可搜索了。", Toast.LENGTH_SHORT).show();
  138. }
  139. }
  140. private TextView.OnEditorActionListener mWriteListener = new TextView.OnEditorActionListener() {
  141. @Override
  142. public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
  143. if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
  144. String message = view.getText().toString();
  145. sendMessage(message);
  146. }
  147. return true;
  148. }
  149. };
  150. private final Handler mHandler = new Handler() {
  151. @Override
  152. public void handleMessage(Message msg) {
  153. switch (msg.what) {
  154. case MESSAGE_STATE_CHANGE:
  155. switch (msg.arg1) {
  156. case ChatService.STATE_CONNECTED:
  157. mTitle.setText(R.string.title_connected_to);
  158. mTitle.append(mConnectedDeviceName);
  159. mConversationArrayAdapter.clear();
  160. break;
  161. case ChatService.STATE_CONNECTING:
  162. mTitle.setText(R.string.title_connecting);
  163. break;
  164. case ChatService.STATE_LISTEN:
  165. case ChatService.STATE_NONE:
  166. mTitle.setText(R.string.title_not_connected);
  167. break;
  168. }
  169. break;
  170. case MESSAGE_WRITE:
  171. byte[] writeBuf = (byte[]) msg.obj;
  172. String writeMessage = new String(writeBuf);
  173. mConversationArrayAdapter.add("我: " + writeMessage);
  174. break;
  175. case MESSAGE_READ:
  176. byte[] readBuf = (byte[]) msg.obj;
  177. String readMessage = new String(readBuf, 0, msg.arg1);
  178. mConversationArrayAdapter.add(mConnectedDeviceName + ": "
  179. + readMessage);
  180. break;
  181. case MESSAGE_DEVICE_NAME:
  182. mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
  183. Toast.makeText(getActivity().getApplicationContext(),"链接到 " + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
  184. break;
  185. case MESSAGE_TOAST:
  186. Toast.makeText(getActivity().getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show();
  187. break;
  188. }
  189. }
  190. };
  191. //返回进入好友列表操作后的数回调方法
  192. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  193. switch (requestCode) {
  194. case REQUEST_CONNECT_DEVICE:
  195. if (resultCode == Activity.RESULT_OK) {
  196. String address = data.getExtras().getString(DeviceList.EXTRA_DEVICE_ADDRESS);
  197. BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
  198. mChatService.connect(device);
  199. }else if(resultCode==Activity.RESULT_CANCELED){
  200. Toast.makeText(view.getContext(), "未选择任何好友!", Toast.LENGTH_SHORT).show();
  201. }
  202. break;
  203. case REQUEST_ENABLE_BT:
  204. if (resultCode == Activity.RESULT_OK) {
  205. setupChat();
  206. } else {
  207. Toast.makeText(view.getContext(), R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
  208. getActivity().finish();
  209. }
  210. }
  211. }
  212. //内部类,选项菜单的单击事件处理
  213. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  214. private class MyMenuItemClickListener implements Toolbar.OnMenuItemClickListener {
  215. @Override
  216. public boolean onMenuItemClick(MenuItem item) {
  217. switch (item.getItemId()) {
  218. case R.id.scan:
  219. //启动DeviceList这个Activity
  220. Intent serverIntent = new Intent(getActivity(), DeviceList.class);
  221. startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
  222. return true;
  223. case R.id.discoverable:
  224. ensureDiscoverable();
  225. return true;
  226. case R.id.back:
  227. getActivity().finish();
  228. System.exit(0);
  229. return true;
  230. }
  231. return false;
  232. }
  233. }
  234. @Override
  235. public synchronized void onResume() {
  236. //synchronized:同步方法实现排队调用
  237. super.onResume();
  238. if (mChatService != null) {
  239. if (mChatService.getState() == ChatService.STATE_NONE) {
  240. mChatService.start();
  241. }
  242. }
  243. }
  244. @Override
  245. public void onDestroy() {
  246. super.onDestroy();
  247. if(mChatService != null){
  248. mChatService.stop();
  249. }
  250. }
  251. }

8.源码地址

https://github.com/xixia7/BlueTooth_Chat.git

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/306540
推荐阅读
相关标签
  

闽ICP备14008679号