问题
在绑定蓝牙获取服务时,发现以下问题:
bindService()绑定蓝牙服务,onServiceConnected()未被触发。
在我的 Demo 中
说明:Service服务 中 未开启多线程
//...
/**
* 成功连接到服务则会触发onServiceConnected回调
*/
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ((BluetoothLeService.LocalBinder) service).getBLEService();// 程序走到这里之后,就得到了 Service 对象
mBluetoothGattService = mService.getBluetoothGattService();
mBluetoothGattCharacteristic = mService.getBluetoothGattCharacteristic();
}
};
//...
@Override
public void onStart() {
super.onStart();
final int BIND_AUTO_CREATE = 0x0001;
//获取 BLE 服务实例
Intent bindIntent = new Intent(getActivity(), BluetoothLeService.class);
boolean bind = getActivity().bindService(bindIntent, connection, BIND_AUTO_CREATE);
Log.i("Display BindService",""+bind);
}
@Override
public void onResume() {
super.onResume();
//读取设备信息
Read_Information_Data();
//读取实时信息
Read_Realtime_Data();
}
/**
* 蓝牙解绑
*/
@Override
public void onStop() {
super.onStop();
getActivity().unbindService(connection);
}
onResume()中发送读指令操作失败。
通过 Debug 发现我们并未获取蓝牙服务实例,即onServiceConnected()未执行。
解决方法
查阅 官方文档,发现了这么一段描述:
注意,根据文档所述,我们bindService()的返回值只是表明服务是否存在;真正能代表是否成功绑定服务的是触发onServiceConnected()回调。
为此 我先想到的解决方法是
while(true){
//等待取得mService
if(null != mService){
//获得服务后的一些处理
}
}
然而这样如果是在 UI线程 中绑定服务,绑定后就进入死循环等待。这时就会出现 “未响应”的错误。
因为 UI线程 被阻塞了,我们一直等不到重写的onServiceConnected()方法被调用。
文档中还有这样一段描述:
所谓异步操作,即我们执行完bindService(),并 不会马上回调onServiceConnected(),当然也不会等待它;而是按照程序的顺序继续执行。这时由于服务还未成功获取到,我们调用服务中的方法就会出错。
目前我的 解决方法:
绑定后开启一个线程去判断是否已获取服务,如果以获取进行相应的处理。如果未获取,则延时后再判断。
new Thread(new Runnable() {
@Override
public void run() {
while(true){
Utils.delay(20);
//等待取得mService
if(null != mService){
//...
}
//...
}
}
}).start();
当然还有一种方法,即官方例程提供的做法:
在按键监听事件中使用
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(connection);
mBound = false;
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
结语
如果有不一样的解决方法,欢迎留言交流~
如果有说得不对得地方,还望指正~