Android蓝牙开发求助:如何从MainActivity调用Bluetooth Service读写
搞定Android MainActivity与Bluetooth Service的蓝牙读写交互
我来帮你理顺这个问题!你已经成功连上了ALEA蓝牙模块,接下来核心就是让MainActivity和后台的Bluetooth Service做好通信——一个负责发起读写请求,一个负责实际和蓝牙模块打交道。下面我给你一套可落地的实现方案:
第一步:给你的Bluetooth Service补全核心能力
首先,Service要承担蓝牙连接的维护、读写线程的管理,还要给Activity提供交互的入口(比如Binder),同时把读取到的数据通知给Activity。这里是核心代码示例:
public class BluetoothService extends Service { private static final String TAG = "BluetoothService"; private BluetoothSocket mmSocket; private InputStream mmInStream; private OutputStream mmOutStream; private Thread mmReadThread; private boolean mIsConnected = false; // 用Binder让Activity拿到Service实例 private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { BluetoothService getService() { return BluetoothService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } // 连接成功后初始化输入输出流,启动读线程 public void initAfterConnect(BluetoothSocket socket) { this.mmSocket = socket; try { mmInStream = mmSocket.getInputStream(); mmOutStream = mmSocket.getOutputStream(); mIsConnected = true; startReadLoop(); } catch (IOException e) { Log.e(TAG, "初始化流失败", e); disconnect(); } } // 后台循环读取蓝牙数据,读到后发给Activity private void startReadLoop() { mmReadThread = new Thread(() -> { byte[] buffer = new byte[1024]; int bytesRead; while (mIsConnected && mmInStream != null) { try { bytesRead = mmInStream.read(buffer); if (bytesRead > 0) { String received = new String(buffer, 0, bytesRead); // 用本地广播把数据传给MainActivity Intent dataIntent = new Intent("BLUETOOTH_DATA_RECEIVED"); dataIntent.putExtra("content", received); LocalBroadcastManager.getInstance(this).sendBroadcast(dataIntent); } } catch (IOException e) { Log.e(TAG, "读取数据出错,连接可能已断开", e); break; } } mIsConnected = false; }); mmReadThread.start(); } // 对外暴露的发送方法,Activity直接调用这个发字符 public boolean sendChar(String charToSend) { if (!mIsConnected || mmOutStream == null) return false; try { mmOutStream.write(charToSend.getBytes()); mmOutStream.flush(); return true; } catch (IOException e) { Log.e(TAG, "发送字符失败", e); return false; } } // 断开连接的清理方法 public void disconnect() { mIsConnected = false; try { if (mmInStream != null) mmInStream.close(); if (mmOutStream != null) mmOutStream.close(); if (mmSocket != null) mmSocket.close(); } catch (IOException e) { Log.e(TAG, "断开连接时出错", e); } } // 给Activity判断连接状态用的方法 public boolean isConnected() { return mIsConnected; } }
第二步:让MainActivity和Service绑定并交互
Activity需要通过绑定Service拿到实例,然后调用sendChar发送字符,同时注册广播接收Service传来的读取数据。这里是MainActivity的核心实现:
public class MainActivity extends AppCompatActivity { private BluetoothService mBluetoothService; private boolean mIsServiceBound = false; private BroadcastReceiver mDataReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 注册广播接收蓝牙发来的数据 setupDataReceiver(); // 绑定Bluetooth Service bindBluetoothService(); // 示例:点击按钮发送字符'A' Button sendBtn = findViewById(R.id.btn_send_char); sendBtn.setOnClickListener(v -> { if (mIsServiceBound && mBluetoothService.isConnected()) { boolean sendSuccess = mBluetoothService.sendChar("A"); Toast.makeText(this, sendSuccess ? "发送成功" : "发送失败", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "蓝牙未连接", Toast.LENGTH_SHORT).show(); } }); } private void setupDataReceiver() { mDataReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if ("BLUETOOTH_DATA_RECEIVED".equals(intent.getAction())) { String receivedData = intent.getStringExtra("content"); // 回到UI线程更新界面,比如显示在TextView runOnUiThread(() -> { TextView receivedTv = findViewById(R.id.tv_received_data); receivedTv.setText("收到模块数据: " + receivedData); }); } } }; LocalBroadcastManager.getInstance(this).registerReceiver( mDataReceiver, new IntentFilter("BLUETOOTH_DATA_RECEIVED")); } private void bindBluetoothService() { Intent serviceIntent = new Intent(this, BluetoothService.class); bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } // Service绑定的回调,拿到Service实例 private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { BluetoothService.LocalBinder binder = (BluetoothService.LocalBinder) service; mBluetoothService = binder.getService(); mIsServiceBound = true; // 这里可以把之前连接好的BluetoothSocket传给Service初始化 // 比如你之前连接成功后拿到了socket,就调用mBluetoothService.initAfterConnect(socket); } @Override public void onServiceDisconnected(ComponentName arg0) { mIsServiceBound = false; mBluetoothService = null; } }; @Override protected void onDestroy() { super.onDestroy(); // 解绑Service和注销广播,避免内存泄漏 if (mIsServiceBound) { unbindService(mServiceConnection); mIsServiceBound = false; } LocalBroadcastManager.getInstance(this).unregisterReceiver(mDataReceiver); } }
几个关键注意事项
- 权限要到位:别忘了申请
BLUETOOTH、BLUETOOTH_ADMIN,Android 6.0以上还要申请ACCESS_FINE_LOCATION并在运行时请求,不然蓝牙连接会失败。 - 线程不能乱:蓝牙读写绝对不能在UI线程做,Service里的读线程已经帮你处理了后台执行,Activity里更新UI一定要用
runOnUiThread。 - 状态要判断:每次发送前先检查Service是否绑定、蓝牙是否连接,避免空指针或者无效操作。
- 异常要处理:读写过程中可能出现IO异常(比如模块断开),一定要捕获并处理,比如标记连接状态为断开。
这样一套下来,你就能在MainActivity里轻松给ALEA模块发字符,还能接收模块传回的数据啦!
内容的提问来源于stack exchange,提问作者stéphane Guillemot




