[Android] Por qué funciona y salta excepcion del error.

Iniciado por kondrag_X1, 27 Julio 2015, 11:58 AM

0 Miembros y 1 Visitante están viendo este tema.

kondrag_X1

Hola,
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 (java) [Seleccionar]

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
{
   public static final String ACTION_DEVICE_CONNECTED = "pfc.teleco.upct.es.iot_ble.DEVICE_FOUND";

   private static HandlerBLE mHandlerBLE;
   private static Context mContext;
   private static BluetoothDevice mDevice;
   private static String mDeviceAddress;
   private static BluetoothGatt mGatt;
   private static BluetoothAdapter mBlueAdapter = null;

   public static boolean isScanning = false;

   //###################################################################
   /****************** Constructor                **********************/
   //###################################################################
   public HandlerBLE(Context context)
   {
       mContext = context;
       mDeviceAddress= null;
   }

   //###################################################################
   /*********************  statics methods   **************************/
   //###################################################################
   public static HandlerBLE getInstance(Context context) {
       if(mHandlerBLE==null){
           mHandlerBLE = new HandlerBLE(context);
           setup();
       }
       return mHandlerBLE;
   }

   public static void  resetHandlerBLE()
   {
       mDeviceAddress=null;
       disconnect();
       mGatt=null;
   }

   public static void setup()
   {
       BluetoothManager manager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
       mBlueAdapter = manager.getAdapter();
   }

   //###################################################################
   /********************* methods bluetooth                *************/
   //###################################################################

   public void setDeviceAddress(String address) {
       mDeviceAddress=address;
   }

   public String getDeviceAddress() {
       return mDeviceAddress;
   }

   public void startLeScan()
   {
       try
       {
           mBlueAdapter.startLeScan(this);//deprecated
           isScanning = true;
           //mBlueAdapter.startDiscovery();
       }
       catch (Exception e)
       {
           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;
       }
       catch(Exception e)
       {
           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");
           } catch(Exception ex){};
       }
       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 (java) [Seleccionar]

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 (java) [Seleccionar]

public class ScanActivity extends ListActivity implements OnItemClickListener{ //}, LeScanCallback {

   private static final int SCAN_ITEM = 1;
   private static ListView mListView;
   private static Context mContext;
   private static HandlerBLE mHandlerBLE;
   private Handler mHandler;
   private static MySimpleArrayAdapter mAdapter;
   private static List<BluetoothDevice> mDeviceList;
   private Menu mMenu;
   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.setVisibility(View.VISIBLE);

       mListView.setAdapter(mAdapter);
       mListView.setOnItemClickListener(this);

   }

   @Override
   public boolean onCreateOptionsMenu(Menu menu)
   {
       super.onCreateOptionsMenu(menu);
       mMenu = menu;
       String menuTitle= getResources().getString(R.string.scan);
       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
   public boolean onOptionsItemSelected(MenuItem item)
   {
       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.
   public void onItemClick(AdapterView<?> parent, View view, int position, long id)
   {
       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");
       }

       String address  = mDeviceList.get(position).getAddress();
       String name     = mDeviceList.get(position).getName();

       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
   private Runnable mStopRunnable = new Runnable() {
       @Override
       public void run() {
           mHandlerBLE.stopLeScan();
           configureScan(false);
           if (Constant.DEBUG)
               Log.i(Constant.TAG, "Stop scanning");
       }
   };

   public void configureScan(boolean flag)
   {
       //isScanning      = flag;
       String itemText = null;

       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> {
       private final Context context;

       public MySimpleArrayAdapter(Context context, List<BluetoothDevice> deviceList)
       {
           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 (java) [Seleccionar]

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 (java) [Seleccionar]

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
   public void onReceive(Context context, Intent intent)
   {
       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();

               mActivity.runOnUiThread(new Runnable() {
                   @Override
                   public void run() {
                       mAdapter.add(deviceFound);
                       mAdapter.notifyDataSetChanged();

                       if (Constant.DEBUG)
                           Log.i(Constant.TAG, "Added new device " + deviceFound.getAddress() + " --- Name: " + deviceFound.getName());
                   }
               });
           }catch(Exception e)
           {
               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.

Usuario Invitado

Has dado demasiado info de cómo funciona, pero no de la excepción objeto de este tema. Pega el rastreo de pila para analizarlo. Otra cosa, ¿la clase que hereda de BroadcastReceiver es interna? Si es así, necesitas hacerla estática. Una clase interna es asociada a una instancia de la clase externa.
"La vida es muy peligrosa. No por las personas que hacen el mal, si no por las que se sientan a ver lo que pasa." Albert Einstein

kondrag_X1

Tienes toda la razón. Me explico mejor o trato de hacerlo.

El error, basicamente, es que me salta la excepción
Código (java) [Seleccionar]

catch(Exception e)
            {
                Log.i(Constant.TAG,"[Error(BLEBroadcastReceiver)]: "+e.getCause()+"\n"+e.getStackTrace()+"\n"+e.getLocalizedMessage());
            }


de la clase BLEBroadcastReceiver, la cual es publica y no pertenece al mismo paquete que la activity, la activity está en el paquete GUI.

El error parece debido a la clase Parcelable de la serialización pero no lo que entiendo es a que se debe el fallo.


07-27 17:18:26.156    8954-9111/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ ServiceDetectionTag Start scanning
07-27 17:18:41.156    8954-9111/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ ServiceDetectionTag Stop scanning
07-27 17:18:53.537    8954-8954/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ BLEBroadcastReceiver -- OnReceive() -> Added new device EC:4A:C2:B4:E3:B4 --- Name: I'm Flowmeter!!
07-27 17:18:56.156    8954-9111/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ ServiceDetectionTag Start scanning
07-27 17:18:57.287    8954-8971/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ (HandlerBLE) -- onLeScan -> throwing information to the listener.
07-27 17:19:07.781    8954-8954/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ BLEBroadcastReceiver -- OnReceive() -> BroadcastReceiver new device found.
07-27 17:19:09.139    8954-8954/pfc.teleco.upct.es.iot_ble I/IoT-APP﹕ [Error(BLEBroadcastReceiver)]:
    Message: null
    Cause:null
    StackTrace[Ljava.lang.StackTraceElement;@41d76bb0
    null


y este es el error que obtengo

    java.lang.SecurityException: need INSTALL_LOCATION_PROVIDER permission, or UID of a currently bound location provider
            at android.os.Parcel.readException(Parcel.java:1465)
            at android.os.Parcel.readException(Parcel.java:1419)
            at android.location.ILocationManager$Stub$Proxy.reportLocation(ILocationManager.java:1122)
            at com.android.location.provider.LocationProviderBase.reportLocation(LocationProviderBase.java:137)
            at com.google.android.location.network.NetworkLocationService.onHandleIntent(SourceFile:99)
            at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
            at android.os.Handler.dispatchMessage(Handler.java:110)
            at android.os.Looper.loop(Looper.java:193)
            at android.os.HandlerThread.run(HandlerThread.java:61)


Usuario Invitado

#3
A mí me suena más a un bug con el tipo de dispositivo. Lo que no entiendo, es por qué cuando especificas un constructor vacío no sucede. De alguna manera, ese super() hace algo en el constructor de BroadcasterReceiver o el runtime instancia internamente a las subclases de BroadcasterReceiver y al no encontrar un constructor vacío, lanza excepción. Es raro, trata de reportarlo como bug a ver qué te dicen xD.
"La vida es muy peligrosa. No por las personas que hacen el mal, si no por las que se sientan a ver lo que pasa." Albert Einstein

kondrag_X1

ok muchísimas gracias. Yo pensaba que estaba haciendo un uso indebido del BroadcasterReceiver.