Android蓝牙连接后台保持及跨Activity数据发送实现求助
解决Android全应用蓝牙持续连接及跨Activity发数据问题
你的核心问题是Activity生命周期限制导致蓝牙连接随页面销毁而断开,解决思路是把蓝牙核心逻辑迁移到后台Service中,因为Service独立于Activity生命周期,能在后台保持连接;同时通过组件绑定实现跨Activity调用蓝牙操作。下面是具体实现方案:
一、核心改造思路
- 将蓝牙扫描、配对、连接、收发数据的逻辑从
BTActivity移到前台Service(避免系统因内存不足杀死后台服务)。 - 用
Binder实现Activity与Service的绑定,让任意Activity能获取Service实例并调用蓝牙方法。 - 通过广播或LiveData实现Service向Activity传递蓝牙接收的数据。
二、具体代码实现
1. 创建蓝牙后台服务BluetoothService
这个Service负责所有蓝牙操作,并且在后台持续运行:
public class BluetoothService extends Service { private BluetoothAdapter mBluetoothAdapter; private BluetoothSocket mBluetoothSocket; private InputStream mInputStream; private OutputStream mOutputStream; private Thread mReceiveThread; private boolean mIsConnected = false; // 用于Activity绑定Service的Binder private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { BluetoothService getService() { return BluetoothService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 启动前台服务(Android 8.0+必须,避免被系统杀死) startForeground(1, createForegroundNotification()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // Service被杀死后尝试重启 return START_STICKY; } // 创建前台通知(低优先级,不打扰用户) private Notification createForegroundNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( "bluetooth_channel", "蓝牙服务", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } return new NotificationCompat.Builder(this, "bluetooth_channel") .setContentTitle("蓝牙连接中") .setContentText("保持设备连接") .setSmallIcon(R.drawable.ic_bluetooth) .build(); } // 对外暴露的连接设备方法 public boolean connectDevice(BluetoothDevice device) { if (mIsConnected) return true; try { // 替换为你使用的蓝牙UUID UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); mBluetoothSocket = device.createRfcommSocketToServiceRecord(uuid); mBluetoothAdapter.cancelDiscovery(); // 断开扫描避免影响连接 mBluetoothSocket.connect(); mInputStream = mBluetoothSocket.getInputStream(); mOutputStream = mBluetoothSocket.getOutputStream(); mIsConnected = true; startReceiveThread(); // 启动数据接收线程 return true; } catch (IOException e) { e.printStackTrace(); disconnect(); return false; } } // 数据接收线程 private void startReceiveThread() { mReceiveThread = new Thread(() -> { byte[] buffer = new byte[1024]; int bytes; while (mIsConnected && mInputStream != null) { try { bytes = mInputStream.read(buffer); if (bytes > 0) { String receivedData = new String(buffer, 0, bytes); // 发送广播通知Activity收到数据 sendDataBroadcast(receivedData); } } catch (IOException e) { e.printStackTrace(); disconnect(); break; } } }); mReceiveThread.start(); } // 对外暴露的发送数据方法 public boolean sendData(String data) { if (!mIsConnected || mOutputStream == null) return false; try { mOutputStream.write(data.getBytes()); mOutputStream.flush(); return true; } catch (IOException e) { e.printStackTrace(); return false; } } // 断开连接 public void disconnect() { mIsConnected = false; try { if (mInputStream != null) mInputStream.close(); if (mOutputStream != null) mOutputStream.close(); if (mBluetoothSocket != null) mBluetoothSocket.close(); } catch (IOException e) { e.printStackTrace(); } if (mReceiveThread != null) { mReceiveThread.interrupt(); mReceiveThread = null; } } // 发送广播传递数据给Activity private void sendDataBroadcast(String data) { Intent intent = new Intent("bluetooth_data_received"); intent.putExtra("data", data); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } @Override public void onDestroy() { super.onDestroy(); disconnect(); stopForeground(true); } }
2. 改造原BTActivity绑定Service
原Activity不再直接处理蓝牙逻辑,改为绑定Service并调用其方法:
public class BTActivity extends AppCompatActivity { private BluetoothService mBluetoothService; private boolean mIsServiceBound = false; private BroadcastReceiver mDataReceiver; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { BluetoothService.LocalBinder binder = (BluetoothService.LocalBinder) service; mBluetoothService = binder.getService(); mIsServiceBound = true; // 这里可以初始化蓝牙操作,比如连接已配对设备 // mBluetoothService.connectDevice(selectedDevice); } @Override public void onServiceDisconnected(ComponentName name) { mBluetoothService = null; mIsServiceBound = false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bt); // 启动并绑定Service Intent serviceIntent = new Intent(this, BluetoothService.class); startService(serviceIntent); // 确保Service后台运行 bindService(serviceIntent, mServiceConnection, BIND_AUTO_CREATE); // 注册广播接收器接收蓝牙数据 mDataReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String data = intent.getStringExtra("data"); updateReceivedDataUI(data); // 更新UI显示数据 } }; LocalBroadcastManager.getInstance(this) .registerReceiver(mDataReceiver, new IntentFilter("bluetooth_data_received")); } // 示例:发送数据按钮点击事件 public void onSendDataClick(View view) { if (mIsServiceBound && mBluetoothService != null) { boolean success = mBluetoothService.sendData("来自BTActivity的消息"); Toast.makeText(this, success ? "发送成功" : "发送失败", Toast.LENGTH_SHORT).show(); } } private void updateReceivedDataUI(String data) { TextView tvReceived = findViewById(R.id.tv_received_data); tvReceived.setText(data); } @Override protected void onDestroy() { super.onDestroy(); // 解绑Service,但Service仍在后台运行 if (mIsServiceBound) { unbindService(mServiceConnection); mIsServiceBound = false; } LocalBroadcastManager.getInstance(this).unregisterReceiver(mDataReceiver); } }
3. 其他Activity调用蓝牙发送数据
任意Activity只需绑定Service,即可调用发送方法:
public class OtherActivity extends AppCompatActivity { private BluetoothService mBluetoothService; private boolean mIsServiceBound = false; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { BluetoothService.LocalBinder binder = (BluetoothService.LocalBinder) service; mBluetoothService = binder.getService(); mIsServiceBound = true; } @Override public void onServiceDisconnected(ComponentName name) { mBluetoothService = null; mIsServiceBound = false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_other); // 绑定Service Intent serviceIntent = new Intent(this, BluetoothService.class); bindService(serviceIntent, mServiceConnection, BIND_AUTO_CREATE); } // 示例:其他页面发送数据 public void sendDataFromOtherPage(View view) { if (mIsServiceBound && mBluetoothService != null) { boolean success = mBluetoothService.sendData("来自OtherActivity的消息"); Toast.makeText(this, success ? "发送成功" : "发送失败", Toast.LENGTH_SHORT).show(); } } @Override protected void onDestroy() { super.onDestroy(); if (mIsServiceBound) { unbindService(mServiceConnection); mIsServiceBound = false; } } }
三、关键配置与注意事项
- 权限配置:在
AndroidManifest.xml中添加蓝牙相关权限:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 扫描需要 --> <!-- Android 12+ 额外权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 声明Service --> <service android:name=".BluetoothService" />
- LiveData替代广播:如果需要生命周期感知的数据传递,可以在Service中使用
MutableLiveData,Activity通过观察LiveData接收数据,比广播更安全简洁。 - 异常处理:建议在Service中添加蓝牙连接状态的回调,让Activity能实时感知连接状态变化。
内容的提问来源于stack exchange,提问作者Bhanu Prakash Pasupula
相关产品推荐
相关产品推荐

