Autore Topic: Classe helper per la comunicazione con un servizio remoto  (Letto 516 volte)

Offline Gianluca

  • Nuovo arrivato
  • *
  • Post: 19
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S-II (i9100)
  • Sistema operativo:
    Windows 8
Classe helper per la comunicazione con un servizio remoto
« il: 30 Agosto 2013, 18:45:03 CEST »
0
Buonsalve a tutti!

Ho costruito un servizio (UpdateService), che implementa un'interfaccia AIDL (IUpdateService); esso ogni tot tempo (usando un timer) fa il refresh del DB fetchando alcuni dati dalla rete.
Tutto funziona alla grande, fino a che non voglio che il servizio remoto non dialoghi con le activity in foreground, e viceversa. In pratica, l'utente dovrebbe avere la possibilità di far partire il refresh dei dati quando vuole lui (chiaramente).
Siccome connettere ogni singola classe al servizio mi sembrava esoso, ingombrante, di difficile mantenimento e manutenzione, ma soprattutto rischioso (ho bisogno di questo codice in una fragmentActivity e in un fragment al suo interno), ho decido di esternalizzare il tutto in un helper che trovate qui sotto.
Fatto ciò, ho definito un Broadcast Receiver che venga richiamato quando l'update è concluso.

Codice (Java): [Seleziona]
public class ServiceManager  {
       
        private Context mContext;
        private static IUpdateService sService = null;
        private static ServiceManager sSingleton = null;
        private ServiceConnection sConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder boundService) {
                        Util.log("onServiceConnected() connected");
                        sService = IUpdateService.Stub.asInterface((IBinder) boundService);
                }
                @Override
                public void onServiceDisconnected(ComponentName name) {
                        Util.log("onServiceConnected() disconnected");
                        sService = null;
                }
        };
       
        private ServiceManager(Context context) {
                // Creazione della classe
                // setto il contesto
                mContext = context;
               
                // avvio il servizio
                mContext.startService(new Intent(mContext, UpdateService.class));
        }
       
        public static synchronized ServiceManager getInstance(Context context) {
                if (sSingleton == null)
                        sSingleton = new ServiceManager(context);
                return sSingleton;
        }

        public void updateDB(final Callable<Void> func) {
                Util.log("updateDB manager called");
                initService();
               
                try {
                        BroadcastReceiver receiver = new BroadcastReceiver() {
                            // Quando il servizio ha concluso di fare l'update
                            // viene notificato questo receiver
                            @Override
                            public void onReceive(Context context, Intent intent) {
                                Bundle bundle = intent.getExtras();
                                if (bundle != null) {
                                    Integer result_code = bundle.getInt(UpdateService.RESULT_CODE);
                                    if (result_code == 1) {
                                        try {
                                            func.call();
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                                mContext.unregisterReceiver(this);
                            }
                        };
                        mContext.registerReceiver(receiver, new IntentFilter(UpdateService.NOTIFICATION));

                        // Update vero e proprio
                        sService.updateDB();
                } catch (Exception e) {
                        e.printStackTrace();
                }
               
                releaseService();
        }
       
        public void unbound() {
                releaseService();
        }        
       
        /** Binds this activity to the service. */
        private void initService() {
                Util.log("Binding...");
                mContext.bindService(new Intent(mContext, IUpdateService.class), sConnection, Context.BIND_AUTO_CREATE);
        }
       
        /** Unbinds this activity from the service. */
        private void releaseService() {
                if (sConnection!=null) {
                        mContext.unbindService(sConnection);
                        sConnection = null;
                        Util.log("Service unbound.");
                }
        }
}

Purtroppo, chiamando updateDB() dal fragment mi becco un NPE: sService non è ancora definito (e quindi onServiceConnected non ancora chiamato).
Il broadcast receiver invece funziona alla grande, per fortuna... È tutto un problema di connessione al servizio remoto.
Pare sia un problema noto, ma anche provando varie soluzione proposte sono ancora fermo.

Codice: [Seleziona]
08-29 03:26:06.820: W/System.err(15970): java.lang.NullPointerException
08-29 03:26:06.820: W/System.err(15970):         at com.example.services.ServiceManager.updateDB(ServiceManager.java:79)

Siccome non sono del tutto sicuro che sia il metodo migliore per interagire con i servizi remoti, non è che mi dareste qualche feedback? Suggerimenti differenti?

Quello a cui voglio arrivare sarebbe questo:
SERVICE: espleta una funzione (updateDB()) periodicamente; vorrei fosse remoto, affinché non venga killato dal sistema. Una sorta di syncer.
ACTIVITY: sa che il SERVICE sta aggiornando; permette di far partire la funzione arbitrariamente se non sta aggiornando; sa quando il SERVICE ha finito di aggiornare.
« Ultima modifica: 30 Agosto 2013, 20:08:28 CEST da Gianluca »

Offline matttt

Re:Classe helper per la comunicazione con un servizio remoto
« Risposta #1 il: 30 Agosto 2013, 19:05:01 CEST »
0
E se usi un Handler per dialogare con il servizio?
Bound Services | Android Developers
Le mie apps su Google Play Store:

Offline Gianluca

  • Nuovo arrivato
  • *
  • Post: 19
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S-II (i9100)
  • Sistema operativo:
    Windows 8
Re:Classe helper per la comunicazione con un servizio remoto
« Risposta #2 il: 30 Agosto 2013, 20:06:24 CEST »
0
E se usi un Handler per dialogare con il servizio?
Bound Services | Android Developers

Purtroppo non credo funzionerebbe.

Codice (Java): [Seleziona]
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            mBound = true;
        }
        //...
    };

La funzione di callback onServiceConnected() non viene proprio chiamata. Quindi il problema è a monte... :/

Offline matttt

Re:Classe helper per la comunicazione con un servizio remoto
« Risposta #3 il: 30 Agosto 2013, 20:24:33 CEST »
0
Premesso che sarebbe più una "toppa" che una vera soluzione... :)
uscire dalla funzione (o saltare solo la riga service updateDB) se sService == null ?
Così finchè il servizio non è pronto non dovresti avere problemi (anche se ti perderai dei dati, non so se è rilevante o meno questo).
Le mie apps su Google Play Store: