les comento gente estoy haciendo un controlador BLE (Bluetooth low energy). Estoy montando una arquitectura separando el controlador BLE de la GUI y demás pero tengo un error que aunque el programa funciona me gustaría corregirlo.
Os explico:
Tengo una clase para manejar los dispositivos BLE:
Código
package pfc.teleco.upct.es.iot_ble.BLE; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter.LeScanCallback; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.util.Log; import java.util.ArrayList; import java.util.List; import java.util.UUID; import pfc.teleco.upct.es.iot_ble.BEAN.BeanBluetoothDevice; import pfc.teleco.upct.es.iot_ble.Constant; import pfc.teleco.upct.es.iot_ble.DEVICES.Device; public class HandlerBLE implements LeScanCallback { private static HandlerBLE mHandlerBLE; private static BluetoothDevice mDevice; private static BluetoothGatt mGatt; private static BluetoothAdapter mBlueAdapter = null; public static boolean isScanning = false; //################################################################### /****************** Constructor **********************/ //################################################################### { mContext = context; mDeviceAddress= null; } //################################################################### /********************* statics methods **************************/ //################################################################### if(mHandlerBLE==null){ mHandlerBLE = new HandlerBLE(context); setup(); } return mHandlerBLE; } public static void resetHandlerBLE() { mDeviceAddress=null; disconnect(); mGatt=null; } public static void setup() { mBlueAdapter = manager.getAdapter(); } //################################################################### /********************* methods bluetooth *************/ //################################################################### mDeviceAddress=address; } return mDeviceAddress; } public void startLeScan() { try { mBlueAdapter.startLeScan(this);//deprecated isScanning = true; //mBlueAdapter.startDiscovery(); } { Log.i(Constant.TAG,"(HandlerBLE)[Error]:"+e.getStackTrace()+" "+e.getCause()+" "+e.getMessage()+ " "+e.getLocalizedMessage()); } } public void stopLeScan() { try { mBlueAdapter.stopLeScan(this); //deprecated //mBlueAdapter.startDiscovery(); isScanning = false; } { Log.i(Constant.TAG,"(HandlerBLE)[Error]:"+e.getStackTrace()+" "+e.getCause()+" "+e.getMessage()+ " "+e.getLocalizedMessage()); } } /* public void connect() { mDevice = mBlueAdapter.getRemoteDevice(mDeviceAddress); mServices.clear(); if(mGatt!=null){ mGatt.connect(); }else{ mDevice.connectGatt(mContext, false, mCallBack); } } */ public void discoverServices() { if (Constant.DEBUG) Log.i(Constant.TAG, "(HandlerBLE)Scanning services and caracteristics"); mGatt.discoverServices(); } public static void disconnect(){ if (mGatt!=null) { try{ mGatt.disconnect(); mGatt.close(); if (Constant.DEBUG) Log.i(Constant.TAG, "(HandlerBLE)Disconnecting GATT"); } mGatt = null; } public boolean isConnected(){ return (mGatt!=null); } //################################################################### /********************* methods Scan bluetooth *************/ //################################################################### /* * this method is used to receive devices which were found durind a scan*/ @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { if(Constant.DEBUG) Log.i(Constant.TAG,"(HandlerBLE) -- onLeScan -> throwing information to the listener."); //create the packet wich will be sent to listener. Intent intent = new Intent(); intent.setAction(HandlerBLE.ACTION_DEVICE_CONNECTED); BeanBluetoothDevice beanBlue = new BeanBluetoothDevice(); beanBlue.setBluetoothDevice(device); beanBlue.setmRssi(rssi); beanBlue.setmScanRecord(scanRecord); intent.putExtra(Constant.EXTRA_BEAN_BLUETOOTHDEVICE,beanBlue); mContext.sendBroadcast(intent); } }
Esta clase se encargará de controlar el BLE sanea, conexión y comunicación con otros dispositivos. Y a su vez se comunicará con el resto de la aplicación con Broadcastreceiver , es decir en el método implementado en la interfaz onLeScan, me avisa cuando se ha encontrado un dispositivo y envia la señal broadcast.
Como se puede ver tengo un objeto llamado BeanBluetoothDevice este simplemente es una encapsulación de la información que necesito y serializado.
Código
public class BeanBluetoothDevice implements Parcelable { private BluetoothDevice mdevice; private int mRssi; private byte[] mScanRecord; public BeanBluetoothDevice() { super(); } //################################################################### protected BeanBluetoothDevice(Parcel in) { mdevice = in.readParcelable(BluetoothDevice.class.getClassLoader()); mRssi = in.readInt(); mScanRecord = in.createByteArray(); } public static final Creator<BeanBluetoothDevice> CREATOR = new Creator<BeanBluetoothDevice>() { @Override public BeanBluetoothDevice createFromParcel(Parcel in) { return new BeanBluetoothDevice(in); } @Override public BeanBluetoothDevice[] newArray(int size) { return new BeanBluetoothDevice[size]; } }; //################################################################### /****************** gets and sets methods **********************/ //################################################################### public void setBluetoothDevice(BluetoothDevice device) {mdevice = device;} public BluetoothDevice getBluetoothDevice() {return mdevice;} public int getmRssi() { return mRssi; } public void setmRssi(int mRssi) { this.mRssi = mRssi; } public byte[] getmScanRecord() { return mScanRecord; } public void setmScanRecord(byte[] mScanRecord) { this.mScanRecord = mScanRecord; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(mdevice, flags); dest.writeInt(mRssi); dest.writeByteArray(mScanRecord); } }
una vez e envia la señal yo la recojo en la activity que me interesa, en mi casa es la principal para mostrar los dispositivos.
Código
public class ScanActivity extends ListActivity implements OnItemClickListener{ //}, LeScanCallback { private static final int SCAN_ITEM = 1; private static HandlerBLE mHandlerBLE; private Handler mHandler; private static MySimpleArrayAdapter mAdapter; private static List<BluetoothDevice> mDeviceList; private Activity mActivity; private BLEBroadcastReceiver mScanBroadcastReceiver; //################################################################### /****************** metodos del flujo Android. **********************/ //################################################################### @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scan); mActivity = this; mContext = this; mHandler = new Handler() ; mDeviceList = new ArrayList<BluetoothDevice>(); mAdapter = new MySimpleArrayAdapter(mContext, mDeviceList); mScanBroadcastReceiver = new BLEBroadcastReceiver(this,mAdapter); IntentFilter i = new IntentFilter(HandlerBLE.ACTION_DEVICE_CONNECTED); registerReceiver(mScanBroadcastReceiver,i); if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "BLE TECHNOLOGY NOT SUPPORTED ON THIS DEVICE", Toast.LENGTH_SHORT).show(); finish(); } //run service Intent service = new Intent(this, ServiceDetectionTag.class); startService(service); //manejador BLE mHandlerBLE = ((BLE_Application) getApplication()).getmHandlerBLEInstance(this); ((BLE_Application) getApplication()).resetHandlerBLE(); mListView = getListView(); mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(this); } @Override { super.onCreateOptionsMenu(menu); mMenu = menu; menu.add(0,SCAN_ITEM,0,menuTitle); /* // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_scan, menu);*/ return true; } @Override { switch (item.getItemId()){ case SCAN_ITEM: scan(); break; } return true; /* // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item);*/ } @Override protected void onResume() { super.onResume(); //mAdapter.clear(); HandlerBLE.setup(); } @Override protected void onPause() { super.onStop(); //Make sure that there is no pending Callback mHandler.removeCallbacks(mStopRunnable); //stop service Intent service = new Intent(this, ServiceDetectionTag.class); stopService(service); //mAdapter.clear(); if (mHandlerBLE.isScanning) { mHandlerBLE.stopLeScan(); unregisterReceiver(mScanBroadcastReceiver); } } @Override protected void onStop() { super.onStop(); } //################################################################### /****************** metodos manejo Tag **********************/ //################################################################### @Override //recogemos los metodos del tag seleccionado y recogemos los datos. { if (Constant.DEBUG) Log.i(Constant.TAG, "Selected device " + mDeviceList.get(position).getAddress()); if (mHandlerBLE.isScanning) { //stop scanning configureScan(false); mHandlerBLE.stopLeScan(); if (Constant.DEBUG) Log.i(Constant.TAG, "Stop scanning"); } if (name==null) name="unknown"; Intent intentActivity= new Intent(this, DeviceActivity.class); intentActivity.putExtra(Constant.EXTRA_ADDRESS, address); intentActivity.putExtra(Constant.EXTRA_NAME, name); this.startActivity(intentActivity); if (Constant.DEBUG) Log.i(Constant.TAG, "Trying to connect"); //mConnectionManager.connect(address,true); Toast.makeText(this, "Wait for connection to selected device", Toast.LENGTH_LONG).show(); } //Handle automatic stop of LEScan @Override public void run() { mHandlerBLE.stopLeScan(); configureScan(false); if (Constant.DEBUG) Log.i(Constant.TAG, "Stop scanning"); } }; public void configureScan(boolean flag) { //isScanning = flag; if (mHandlerBLE.isScanning) { itemText = getResources().getString(R.string.stopScan); mHandlerBLE.stopLeScan(); if (Constant.DEBUG) Log.i(Constant.TAG, "ScanActivity -- StopScan"); } else { itemText= getResources().getString(R.string.scan); mHandlerBLE.startLeScan(); if (Constant.DEBUG) Log.i(Constant.TAG, "ScanActivity -- StartScan"); } mMenu.findItem(SCAN_ITEM).setTitle(itemText); if (Constant.DEBUG) Log.i(Constant.TAG, "Updated Menu Item. New value: " + itemText); } // Metodo para iniciar el scaneo cuando te llaman manualmente. private void scan() { if (mHandlerBLE.isScanning) { //stop scanning configureScan(false); mHandlerBLE.stopLeScan(); if (Constant.DEBUG) Log.i(Constant.TAG, "Stop scanning"); return; } else { mAdapter.clear(); mAdapter.notifyDataSetChanged(); configureScan(true); if (Constant.DEBUG) Log.i(Constant.TAG, "Start scanning for BLE devices..."); mHandlerBLE.startLeScan(); //automatically stop LE scan after 5 seconds mHandler.postDelayed(mStopRunnable, 30000); } } /* Clase para crear el adaptador de dispositos Bluetooh */ public class MySimpleArrayAdapter extends ArrayAdapter<BluetoothDevice> { { super(context, R.layout.activity_scan_item,R.id.deviceName, deviceList); this.context = context; } } /* @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { String name="unknown"; if (device.getName()!=null) name=device.getName(); final String finalName = name; final String finalAddress = device.getAddress(); if (Constant.DEBUG) Log.i(Constant.TAG, "Found new device "+ finalAddress + " --- Name: " + finalName); final BluetoothDevice finalDevice= device; // This callback from Bluetooth LEScan can arrive at any moment not necessarily on UI thread. // Use this mechanism to update list View mActivity.runOnUiThread(new Runnable() { @Override public void run() { mAdapter.add(finalDevice); mAdapter.notifyDataSetChanged(); if (Constant.DEBUG) Log.i(Constant.TAG, "Added new device "+ finalAddress + " --- Name: " + finalName); } } ); }*/ }
Como se puede ver en el método onCreate doy de alta la señal que espero recibir.
Código
mScanBroadcastReceiver = new BLEBroadcastReceiver(this,mAdapter); IntentFilter i = new IntentFilter(HandlerBLE.ACTION_DEVICE_CONNECTED); registerReceiver(mScanBroadcastReceiver,i);
Ahora nos dirigimos en la clase donde creo que está el problema, es la clase donde se recibe la señal Broadcast y se procesa.
Código
public class BLEBroadcastReceiver extends BroadcastReceiver { private Activity mActivity; private ScanActivity.MySimpleArrayAdapter mAdapter; public BLEBroadcastReceiver(Activity activity, ScanActivity.MySimpleArrayAdapter adapter) { super(); mAdapter = adapter; mActivity = activity; } public BLEBroadcastReceiver() { super(); } @Override { if(Constant.DEBUG) Log.i(Constant.TAG, "ScanActivity -- OnReceive() -> BroadcastReceiver new device found."); //get signal and add new device into MyarrayAdapter if(intent.getAction().equals(HandlerBLE.ACTION_DEVICE_CONNECTED)) { try { BeanBluetoothDevice beanDeviceFound = intent.getExtras().getParcelable(Constant.EXTRA_BEAN_BLUETOOTHDEVICE); final BluetoothDevice deviceFound = beanDeviceFound.getBluetoothDevice(); @Override public void run() { mAdapter.add(deviceFound); mAdapter.notifyDataSetChanged(); if (Constant.DEBUG) Log.i(Constant.TAG, "Added new device " + deviceFound.getAddress() + " --- Name: " + deviceFound.getName()); } }); { Log.i(Constant.TAG,"[Error(BLEBroadcastReceiver)]: "+e.getCause()+"\n"+e.getStackTrace()+"\n"+e.getLocalizedMessage()); } } } }
aquí el problema me aparice cuando quito el constructor vacío, es decir, al que no se le pasan parámetros. Si lo eliminamos en tiempo de ejecución el programa peta y cuando se lo pongo todo corre normalmente, es decir, si me introduce el dispositivo en el arrayList pero a su vez me da una excepción de los objetos mAdapter y mActivity.
¿A qué puede deberse esto? Si al final lo introduce correctamente.
PD: soy un patata programando, no ser muy duros conmigo.