You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

几个关键注意事项

  • 权限要到位:别忘了申请BLUETOOTHBLUETOOTH_ADMIN,Android 6.0以上还要申请ACCESS_FINE_LOCATION并在运行时请求,不然蓝牙连接会失败。
  • 线程不能乱:蓝牙读写绝对不能在UI线程做,Service里的读线程已经帮你处理了后台执行,Activity里更新UI一定要用runOnUiThread
  • 状态要判断:每次发送前先检查Service是否绑定、蓝牙是否连接,避免空指针或者无效操作。
  • 异常要处理:读写过程中可能出现IO异常(比如模块断开),一定要捕获并处理,比如标记连接状态为断开。

这样一套下来,你就能在MainActivity里轻松给ALEA模块发字符,还能接收模块传回的数据啦!

内容的提问来源于stack exchange,提问作者stéphane Guillemot

火山引擎 最新活动