亲宝软件园·资讯

展开

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

ttt汤圆 人气:0

项目要求

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

效果展示

在这里插入图片描述
在这里插入图片描述

项目结构

在这里插入图片描述

主要代码

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;
  }
 }
}

项目地址

点我进入仓库

总结

加载全部内容

相关教程
猜你喜欢
用户评论