我正在尝试在同一个Android界面上管理我的BLE设备的温度服务和LED服务。但是,每当我尝试打开LED时,应用程序就会死机,我不知道如何解决这个问题(我不擅长Java或BLE)。
我拥有我需要的所有特征和服务的UUID。我正在尝试创建两个BluetoothGattCharacteristic
private BluetoothGattCharacteristic characteristic;
private BluetoothGattCharacteristic characteristicCTRL2;
当我调用温度特性并进行温度测量时,它工作正常。但是,每当我尝试打开LED /调用LED特性时,应用程序就会死机。
10-30 08:48:33.026 1033-1033/com.example.android.bluetoothlegatt E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.bluetoothlegatt, PID: 1033
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.bluetooth.BluetoothGattCharacteristic.setValue(byte[])' on a null object reference
at com.example.android.bluetoothlegatt.AutoConnectActivity.turnLEDon(AutoConnectActivity.java:71)
at com.example.android.bluetoothlegatt.AutoConnectActivity$6.onClick(AutoConnectActivity.java:264)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19754)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5219)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
以下代码是我如何转移所有特征&一起服务。
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.add(gattCharacteristic);
if ( gattCharacteristic.getUuid().equals(UUID.fromString(SampleGattAttributes.CTRL_2))){ //CTRL_2 is the UUID of my LED characteristic
Log.e(TAG, "GattCharacteristics= " + gattCharacteristic.getUuid());
Intent intent = new Intent(DeviceControlActivity.this, AutoConnectActivity.class);
intent.putExtra("character_id", "F000AA01-0451-4000-B000-000000000000"); //UUID of my temperature characteristic
intent.putExtra("service_id", "F000AA00-0451-4000-B000-000000000000");//UUID of my temperature service
intent.putExtra("address", mDeviceAddress);
intent.putExtra("ctrl2_character_id", gattCharacteristic.getUuid().toString()); //UUID of my LED characteristics
intent.putExtra("ctrl2_service_id", gattCharacteristic.getService().getUuid().toString());//UUID of my LED service
startActivity(intent);
}
我失败的部分是我尝试打开LED的功能。
public boolean turnLEDon()
{
byte[] value = {(byte)1};
characteristicCTRL2.setValue(value);//This is the line I failed
boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);
return status;
};
以下是我尝试在同一界面处理两个特征的完整代码。
package com.example.android.bluetoothlegatt;
public class AutoConnectActivity extends Activity {
private BluetoothGattCharacteristic characteristic;
private BluetoothGattCharacteristic characteristicCTRL2;
private String address;
private UUID serviceId;
private UUID charId;
private UUID ctrl2ServiceId;
private UUID ctrl2CharId;
private TextView debugText;
private Button startBtn;
private Button stopBtn;
private Button ctrlStartBtn;
private Button ctrlStopBtn;
private Timer timer = new Timer();
private boolean started = false;
private ArrayAdapter<String> adapter;
private String Entry;
private File file;
private boolean check= true;
private BluetoothLeService mBluetoothLeService;
public boolean turnLEDon()
{
byte[] value = {(byte)1};
characteristicCTRL2.setValue(value);//This is the line I failed
boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);
return status;
};
public boolean turnLEDoff()
{
byte[] value = {(byte)0};
characteristicCTRL2.setValue(value);
boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);
return !status;
};
private TimerTask timerTask = new TimerTask() {
@Override
public void run() {
mBluetoothLeService.connect(address);
}
};
// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
mBluetoothLeService.connect(address);
List<BluetoothGattService> list = mBluetoothLeService.getSupportedGattServices();
for(BluetoothGattService s : list){
if(s.getUuid().compareTo(serviceId) == 0){
if (check==true) {
characteristic = s.getCharacteristic(charId);
mBluetoothLeService.disconnect();
}
return;
}
if(s.getUuid().compareTo(ctrl2ServiceId) == 0){
if(check==false) {
characteristicCTRL2 = s.getCharacteristic(ctrl2CharId);
}
return;
}
}
mBluetoothLeService.disconnect();
Log.e(TAG, "not find device");
debugText.setText("not find device");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService = null;
}
};
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
debugText.setText(action);
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
// if temperature measurement
if (check==true){
mBluetoothLeService.readCharacteristic(characteristic);}
// if control LED
if(check==false){
mBluetoothLeService.readCharacteristic(characteristicCTRL2);
turnLEDon();
}
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action))
{
if (check==false) {
String CTRL_Status = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
adapter.add("CTRL Status: " + CTRL_Status + " " + new Date(System.currentTimeMillis()));
turnLEDoff();
mBluetoothLeService.disconnect();
}
if(check==true) {
String temperature = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
adapter.add("Temperature: " + temperature + "°C " + new Date(System.currentTimeMillis()));
Entry = address + "," + new Date(System.currentTimeMillis()).toString() + "," + temperature.toString() + "\n";
try {
FileOutputStream out = new FileOutputStream(file, true);
out.write(Entry.getBytes());
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mBluetoothLeService.disconnect();
}
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.autoconnect);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Intent intent = getIntent();
address = intent.getStringExtra("address");
serviceId = UUID.fromString(intent.getStringExtra("service_id"));
charId = UUID.fromString(intent.getStringExtra("character_id"));
ctrl2ServiceId = UUID.fromString(intent.getStringExtra("ctrl2_service_id"));
Log.e(TAG, " CTRL2 SERVICE: " + ctrl2ServiceId);
ctrl2CharId = UUID.fromString(intent.getStringExtra("ctrl2_character_id"));
Log.e(TAG, " CTRL2 CHARAC: " + ctrl2CharId);
((TextView)findViewById(R.id.address_txt)).setText(address);
((TextView)findViewById(R.id.service_txt)).setText(serviceId.toString());
((TextView)findViewById(R.id.char_txt)).setText(charId.toString());
debugText = (TextView)findViewById(R.id.debug_txt);
startBtn = (Button)findViewById(R.id.start_btn);
stopBtn = (Button)findViewById(R.id.stop_btn);
ctrlStartBtn = (Button)findViewById(R.id.ctrl_start_btn);
ctrlStopBtn = (Button)findViewById(R.id.ctrl_stop_btn);
ListView listView = (ListView)findViewById(R.id.result_list);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
startBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(started) return;
started = true;
check=true;
//External Storage
String state;
state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File root = Environment.getExternalStorageDirectory();
File Dir = new File(root.getAbsolutePath() + "/MeasurementDataFile");
if (!Dir.exists()) {
Dir.mkdir();
}
file = new File(Dir, "Temperature.csv");
}
if (check==true){
timer.schedule(timerTask, 0, 1000 * 5); }
}
});
stopBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!started) return;
started = false;
check=true;
timer.cancel();
}
});
ctrlStartBtn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
mBluetoothLeService.connect(address);
check=false;
if(started) return;
started = true;
Toast.makeText (getBaseContext(), "Turn CTRL 1 ON", Toast.LENGTH_SHORT).show();
Log.e(TAG, " On?: " + turnLEDon());
}
});
ctrlStopBtn.setOnClickListener(new View.OnClickListener() {
//mBluetoothLeService.connect(address);
@Override
public void onClick(View v) {
if(!started) return;
started = false;
check=false;
Log.e(TAG, " Off?: " + turnLEDoff());
Toast.makeText (getBaseContext(), "Turn CTRL 1 OFF", Toast.LENGTH_SHORT).show();
}
});
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(mGattUpdateReceiver, DeviceControlActivity.makeGattUpdateIntentFilter());
if (mBluetoothLeService != null) {
final boolean result = mBluetoothLeService.connect(address);
Log.d(TAG, "Connect request result=" + result);
}
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
}
答案 0 :(得分:0)
所有BLE调用都是异步的。这真的很痛苦,但我们拥有的东西。所以你不能只写:
{ boolean status = writeCharacteristic(characteristic); 返回状态; }
希望一切顺利。在这种情况下,只能获得 writeCharacteristic 指令的结果,而不是实际的写操作结果!最后,您需要将代码的一部分注册为 BluetoothGattCallback 后代,并阅读 OnCharacteristicWrite 事件,您可以在其中真正了解写操作的结果。
所有BLE读写操作都应该在严格的逐个序列中完成,并且只能从主应用程序线程完成。
不同的设备具有不同的功能和生产力,因此您可以获得不同的结果 - 恕我直言,这不像谷歌决定以这种方式实现BLE堆栈那样奇怪:)
适用于Android的漂亮BLE指南可在此处找到:https://droidcon.de/sites/global.droidcon.cod.newthinking.net/files/media/documents/practical_bluetooth_le_on_android_0.pdf