Android蓝牙写入完成后崩溃问题排查求助
你的问题核心出在StartConfirm继承MainActivity这个设计上,Android中Activity继承意味着创建一个全新的子类实例,而不是复用原MainActivity的实例,这直接导致了两个关键问题:
- Broken pipe错误:StartConfirm作为MainActivity的子类,会初始化自己的
mBluetoothConnection实例,当你从StartConfirm返回主页面后,原MainActivity的updateUi方法调用write时,可能连接已经被StartConfirm实例干扰(比如重复占用蓝牙资源、连接被意外关闭),导致写入失败抛出Broken pipe。 - Receiver未注册异常:MainActivity中应该有一个匿名广播接收器(日志里的
MainActivity$1@dadacf9),子类StartConfirm继承后,会执行父类的生命周期逻辑,可能在子类实例中注册了接收器,但销毁时尝试注销的接收器要么是父类实例的,要么子类的接收器没正确注册,导致系统抛出“Receiver not registered”的异常。
1. 取消StartConfirm对MainActivity的继承
把StartConfirm改成普通的Activity,不要继承MainActivity——继承Activity不是复用页面功能的正确方式,应该用组合模式或者抽离公共功能到工具类。
修改后的StartConfirm代码:
public class StartConfirm extends AppCompatActivity { // 改为继承AppCompatActivity @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.start_confirm_layout); // 不需要getIntent,因为是startActivityForResult启动的 } public void OnStartConfirmation(View view) { Intent toStart = new Intent(); setResult(1, toStart); finish(); } public void OnNoStartConfirmation(View view) { Intent DoNotStart = new Intent(); setResult(0, DoNotStart); finish(); } }
2. 抽离蓝牙功能为独立工具类
把蓝牙连接、写入等逻辑从MainActivity中抽出来,做成单例类,确保整个App中只有一个蓝牙连接实例,避免多Activity实例导致的资源冲突。
示例BluetoothManager类:
public class BluetoothManager { private static BluetoothManager instance; private BluetoothConnection mBluetoothConnection; private BluetoothManager() { // 初始化蓝牙连接逻辑 } public static synchronized BluetoothManager getInstance() { if (instance == null) { instance = new BluetoothManager(); } return instance; } public void write(byte[] bytes) { if (mBluetoothConnection != null) { mBluetoothConnection.write(bytes); } } // 其他蓝牙相关方法:连接、断开等 }
然后在MainActivity的updateUi中调用这个单例:
private void updateUi(int res) { BluetoothManager bluetoothManager = BluetoothManager.getInstance(); if(res == 1) { byte[] bytes = "startup".getBytes(Charset.defaultCharset()); bluetoothManager.write(bytes); } else if (res == 0) { byte[] bytes = "supp".getBytes(Charset.defaultCharset()); bluetoothManager.write(bytes); } else { byte[] bytes = "sywa".getBytes(Charset.defaultCharset()); bluetoothManager.write(bytes); } }
3. 修复广播接收器的生命周期问题
检查MainActivity中广播接收器的注册逻辑,确保在onResume中注册,onPause中注销,避免在Activity销毁时尝试注销未注册的接收器:
示例代码(假设你的接收器是监听蓝牙状态):
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 处理广播逻辑 } }; @Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBluetoothReceiver, filter); } @Override protected void onPause() { super.onPause(); try { unregisterReceiver(mBluetoothReceiver); } catch (IllegalArgumentException e) { // 捕获接收器未注册的异常,避免崩溃 Log.d("MainActivity", "Receiver already unregistered"); } }
4. 检查startActivityForResult的兼容性(可选)
如果你的App目标API级别是30+,startActivityForResult已经被废弃,建议改用registerForActivityResult,不过这不是导致当前崩溃的直接原因,但可以优化代码:
// 在MainActivity中定义 private ActivityResultLauncher<Intent> startConfirmLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() != RESULT_CANCELED) { // 处理结果 int res = result.getResultCode(); startPaintbot = res; updateUi(res); } } ); // 替换原来的OnStartClick方法 public void OnStartClick(View view) { Intent GetStartConfirmation = new Intent(this, StartConfirm.class); startConfirmLauncher.launch(GetStartConfirmation); }
核心问题是错误地通过继承Activity来复用功能,导致多实例冲突。改成独立Activity+单例工具类的方式,既能复用蓝牙功能,又能避免生命周期和资源冲突的问题。同时修复广播接收器的注册注销逻辑,就能解决你遇到的崩溃和Broken pipe错误。
内容的提问来源于stack exchange,提问作者Plexus

