软件编程
位置:首页>> 软件编程>> Android编程>> Android在类微信程序中实现蓝牙聊天功能的示例代码

Android在类微信程序中实现蓝牙聊天功能的示例代码

作者:ttt汤圆  发布时间:2023-09-07 06:36:19 

标签:android,微信,蓝牙,聊天

项目要求

1.初次打开程序时右上角标题栏显示“无连接”,点击旁边的按钮选择“我的好友”,进入配对界面;
2.选择好友之后,返回主界面,标题栏会显示已连接的手机型号;
3.两部手机间可通过蓝牙聊天

效果展示

Android在类微信程序中实现蓝牙聊天功能的示例代码
Android在类微信程序中实现蓝牙聊天功能的示例代码

项目结构

Android在类微信程序中实现蓝牙聊天功能的示例代码

主要代码

1.在清单文件中注册权限


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

2.在文件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>

用于蓝牙会话的服务组件ChatService.java中有三个内部类:AcceptThread(接受新连接)、ConnectThread(发出连接)和ConnectedThread (已连接)。


// 创建监听线程,准备接受新连接。使用阻塞方式,调用 BluetoothServerSocket.accept()
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();
  }
 }
}

/*
 连接线程,专门用来对外发出连接对方蓝牙的请求和处理流程。
 构造函数里通过 BluetoothDevice.createRfcommSocketToServiceRecord() ,
 从待连接的 device 产生 BluetoothSocket. 然后在 run 方法中 connect ,
 成功后调用 BluetoothChatSevice 的 connected() 方法。定义 cancel() 在关闭线程时能够关闭相关socket 。
 */
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();
  }
 }
}

/*
 双方蓝牙连接后一直运行的线程;构造函数中设置输入输出流。
 run()方法中使用阻塞模式的 InputStream.read()循环读取输入流,然后发送到 UI 线程中更新聊天消息。
 本线程也提供了 write() 将聊天消息写入输出流传输至对方,传输成功后回写入 UI 线程。最后使用cancel()关闭连接的 socket
 */
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(weixinFragment.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
   } catch (IOException e) {
    connectionLost();
    break;
   }
  }
 }

public void write(byte[] buffer) {
  try {
   mmOutStream.write(buffer);
   mHandler.obtainMessage(weixinFragment.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }

public void cancel() {
  try {
   mmSocket.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

新建Activity组件DeviceList,实现拾取与之会话的蓝牙设备。本程序供菜单项主界面的选项菜单“我的友好”调用,用于:

(1)显示已配对的好友列表;
(2)搜索可配对的好友进行配对
(3)新选择并配对的蓝牙设备将刷新好友列表

注意:发现新的蓝牙设备并请求配对时,需要对应接受

关键技术:动态注册一个广播接收者,处理蓝牙设备扫描的结果


public class DeviceList extends Activity{
 private BluetoothAdapter mBtAdapter;
 private ArrayAdapter<String> mPairedDevicesArrayAdapter;
 private ArrayAdapter<String> mNewDevicesArrayAdapter;
 public static String EXTRA_DEVICE_ADDRESS = "device_address"; //Mac地址
 //定义广播接收者,用于处理扫描蓝牙设备后的结果
 private final BroadcastReceiver mReceiver = 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) {
     mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
   } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
    if (mNewDevicesArrayAdapter.getCount() == 0) {
     String noDevices = getResources().getText(R.string.none_found).toString();
     mNewDevicesArrayAdapter.add(noDevices);
    }
   }
  }
 };
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.device_list);
  //在被调用活动里,设置返回结果码
  setResult(Activity.RESULT_CANCELED);
  init(); //活动界面
 }
 private void init() {
  Button scanButton = findViewById(R.id.button_scan);
  scanButton.setOnClickListener(new OnClickListener() {
   public void onClick(View v) {
    Toast.makeText(DeviceList.this, R.string.scanning, Toast.LENGTH_LONG).show();
    doDiscovery(); //搜索蓝牙设备
   }
  });
  mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
  mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
  //已配对蓝牙设备列表
  ListView pairedListView =findViewById(R.id.paired_devices);
  pairedListView.setAdapter(mPairedDevicesArrayAdapter);
  pairedListView.setOnItemClickListener(mPaireDeviceClickListener);
  //未配对蓝牙设备列表
  ListView newDevicesListView = findViewById(R.id.new_devices);
  newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
  newDevicesListView.setOnItemClickListener(mNewDeviceClickListener);
  //动态注册广播接收者
  IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  registerReceiver(mReceiver, filter);
  filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  registerReceiver(mReceiver, filter);
  mBtAdapter = BluetoothAdapter.getDefaultAdapter();
  Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
  if (pairedDevices.size() > 0) {
   findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
   for (BluetoothDevice device : pairedDevices) {
    mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
   }
  } else {
   String noDevices = getResources().getText(R.string.none_paired).toString();
   mPairedDevicesArrayAdapter.add(noDevices);
  }
 }
 @Override
 protected void onDestroy() {
  super.onDestroy();
  if (mBtAdapter != null) {
   mBtAdapter.cancelDiscovery();
  }
  this.unregisterReceiver(mReceiver);
 }
 private void doDiscovery() {
  findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
  if (mBtAdapter.isDiscovering()) {
   mBtAdapter.cancelDiscovery();
  }
  mBtAdapter.startDiscovery(); //开始搜索蓝牙设备并产生广播
   //startDiscovery是一个异步方法
  //找到一个设备时就发送一个BluetoothDevice.ACTION_FOUND的广播
 }
 private OnItemClickListener mPaireDeviceClickListener = new 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 OnItemClickListener mNewDeviceClickListener = new 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);
  }
 };
 //回调方法:进入蓝牙配对设置界面返回后执行
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  init(); //刷新好友列表
 }
}

蓝牙会话的主Activity组件程序fragment.java


public class weixinFrament extends Fragment {
public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
public static final int MESSAGE_WRITE = 3;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;
public static final String DEVICE_NAME = "device_name";
public static final String TOAST = "toast";
private static final int REQUEST_CONNECT_DEVICE = 1; //请求连接设备
private static final int REQUEST_ENABLE_BT = 2;
private TextView mTitle;
private ListView mConversationView;
private EditText mOutEditText;
private Button mSendButton;
private String mConnectedDeviceName = null;
private ArrayAdapter<String> mConversationArrayAdapter;
private StringBuffer mOutStringBuffer;
private BluetoothAdapter mBluetoothAdapter = null;
private ChatService mChatService = null;

private View view;

public weixinFrament() {

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
       Bundle savedInstanceState) {

view = inflater.inflate(R.layout.tab_01, container, false);

Toolbar toolbar = view.findViewById(R.id.toolbar);
 setHasOptionsMenu(true);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  if (ContextCompat.checkSelfPermission(this.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
   ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
  }
 }

//创建选项菜单
 toolbar.inflateMenu(R.menu.option_menu);
 //选项菜单监听
 toolbar.setOnMenuItemClickListener(new MyMenuItemClickListener());
 mTitle = view.findViewById(R.id.title_left_text);
 mTitle.setText(R.string.app_name);
 mTitle = view.findViewById(R.id.title_right_text);
 // 得到本地蓝牙适配器
 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 if (mBluetoothAdapter == null) {
  Toast.makeText(view.getContext(), "蓝牙不可用", Toast.LENGTH_LONG).show();
  getActivity().finish();
  return view;
 }
 if (!mBluetoothAdapter.isEnabled()) { //若当前设备蓝牙功能未开启
  Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  startActivityForResult(enableIntent, REQUEST_ENABLE_BT); //
 } else {
  if (mChatService == null) {
   setupChat(); //创建会话
  }
 }

return view;
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 if (grantResults.length > 0) {
  if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
   Toast.makeText(view.getContext(), "未授权,蓝牙搜索功能将不可用!", Toast.LENGTH_SHORT).show();
  }
 }
}

@Override
public synchronized void onResume() { //synchronized:同步方法实现排队调用
 super.onResume();
 if (mChatService != null) {
  if (mChatService.getState() == ChatService.STATE_NONE) {
   mChatService.start();
  }
 }
}

private void setupChat() {
 mConversationArrayAdapter = new ArrayAdapter<String>(view.getContext(), R.layout.message);
 mConversationView = view.findViewById(R.id.in);
 mConversationView.setAdapter(mConversationArrayAdapter);
 mOutEditText = view.findViewById(R.id.edit_text_out);
 mOutEditText.setOnEditorActionListener(mWriteListener);
 mSendButton = view.findViewById(R.id.button_send);
 mSendButton.setOnClickListener(new View.OnClickListener() {
  public void onClick(View v) {
   TextView textview = view.findViewById(R.id.edit_text_out);
   String message = textview.getText().toString();
   sendMessage(message);
  }
 });
 //创建服务对象
 mChatService = new ChatService(view.getContext(), mHandler);
 mOutStringBuffer = new StringBuffer("");
}

@Override
public void onDestroy() {
 super.onDestroy();
 if (mChatService != null)
  mChatService.stop();
}

private void ensureDiscoverable() { //修改本机蓝牙设备的可见性
 //打开手机蓝牙后,能被其它蓝牙设备扫描到的时间不是永久的
 if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
  Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
  //设置在300秒内可见(能被扫描)
  discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
  startActivity(discoverableIntent);
  Toast.makeText(view.getContext(), "已经设置本机蓝牙设备的可见性,对方可搜索了。", Toast.LENGTH_SHORT).show();
 }
}

private void sendMessage(String message) {
 if (mChatService.getState() != ChatService.STATE_CONNECTED) {
  Toast.makeText(view.getContext(), R.string.not_connected, Toast.LENGTH_SHORT).show();
  return;
 }
 if (message.length() > 0) {
  byte[] send = message.getBytes();
  mChatService.write(send);
  mOutStringBuffer.setLength(0);
  mOutEditText.setText(mOutStringBuffer);
 }
}

private TextView.OnEditorActionListener mWriteListener = new TextView.OnEditorActionListener() {
 @Override
 public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
  if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
   //软键盘里的回车也能发送消息
   String message = view.getText().toString();
   sendMessage(message);
  }
  return true;
 }
};
//使用Handler对象在UI主线程与子线程之间传递消息
private final Handler mHandler = new Handler() { //消息处理
 @Override
 public void handleMessage(Message msg) {
  switch (msg.what) {
   case MESSAGE_STATE_CHANGE:
    switch (msg.arg1) {
     case ChatService.STATE_CONNECTED:
      mTitle.setText(R.string.title_connected_to);
      mTitle.append(mConnectedDeviceName);
      mConversationArrayAdapter.clear();
      break;
     case ChatService.STATE_CONNECTING:
      mTitle.setText(R.string.title_connecting);
      break;
     case ChatService.STATE_LISTEN:
     case ChatService.STATE_NONE:
      mTitle.setText(R.string.title_not_connected);
      break;
    }
    break;
   case MESSAGE_WRITE:
    byte[] writeBuf = (byte[]) msg.obj;
    String writeMessage = new String(writeBuf);
    mConversationArrayAdapter.add("我: " + writeMessage);
    break;
   case MESSAGE_READ:
    byte[] readBuf = (byte[]) msg.obj;
    String readMessage = new String(readBuf, 0, msg.arg1);
    mConversationArrayAdapter.add(mConnectedDeviceName + ": "
      + readMessage);
    break;
   case MESSAGE_DEVICE_NAME:
    mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
    Toast.makeText(getActivity().getApplicationContext(), "链接到 " + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
    break;
   case MESSAGE_TOAST:
    Toast.makeText(getActivity().getApplicationContext(),
      msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show();
    break;
  }
 }
};

//返回进入好友列表操作后的数回调方法
public void onActivityResult(int requestCode, int resultCode, Intent data) {
 switch (requestCode) {
  case REQUEST_CONNECT_DEVICE:
   if (resultCode == Activity.RESULT_OK) {
    String address = data.getExtras().getString(DeviceList.EXTRA_DEVICE_ADDRESS);
    BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    mChatService.connect(device);
   } else if (resultCode == Activity.RESULT_CANCELED) {
    Toast.makeText(view.getContext(), "未选择任何好友!", Toast.LENGTH_SHORT).show();
   }
   break;
  case REQUEST_ENABLE_BT:
   if (resultCode == Activity.RESULT_OK) {
    setupChat();
   } else {
    Toast.makeText(view.getContext(), R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
    getActivity().finish();
   }
 }
}

//内部类,选项菜单的单击事件处理
private class MyMenuItemClickListener implements Toolbar.OnMenuItemClickListener {
 @Override
 public boolean onMenuItemClick(MenuItem item) {
  switch (item.getItemId()) {
   case R.id.scan:
    //启动DeviceList这个Activity
    Intent serverIntent = new Intent(weixinFrament.this.getActivity(), DeviceList.class);
    startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
    return true;
   case R.id.discoverable:
    ensureDiscoverable();
    return true;
   case R.id.back:
    getActivity().finish();
    System.exit(0);
    return true;
  }
  return false;
 }
}
}

项目地址

点我进入仓库

来源:https://blog.csdn.net/qq_44921821/article/details/106460714

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com