Autore Topic: Esecuzione di misurazioni ogni X minuti per Y secondi  (Letto 1850 volte)

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Esecuzione di misurazioni ogni X minuti per Y secondi
« il: 27 Luglio 2017, 10:47:07 CEST »
0
Ciao,
devo scrivere una piccola app che ogni X minuti attivi un sensore e legga i dati forniti per un certo numero Y di secondi.
Terminata la lettura dei dati, questi devono essere passati ad un oggetto specifico che li dovrà elaborare.

Ho la necessità di far si che queste letture vengano eseguite indipendentemente se l'app è in foreground, background o se il dispositivo è in standby.

Dal momento che non sono affatto esperto di Android (ho iniziato a studiarlo da poco), avrei bisogno di qualche suggerimento.

Per quanto riguarda l'attivazione del sensore ogni X minuti, pensavo di utilizzare un AlarmManager, visto che a quanto pare fa il suo lavoro in qualsiasi condizione.
Mi chiedevo però cosa fosse conveniente fare per far si che la lettura dei dati dal sensori duri un certo tempo prestabilito (Y secondi).
Inoltre, una volta terminare le misurazioni, immagino che l'allarme venga bloccato fino a che non passa il periodo impostato, quindi come mi conviene passare i dati ottenuti per farli elaborare?

Grazie

Post unito: [time]27 Luglio 2017, 15:49:04 CEST[/time]
Per adesso il codice che ho prodotto (e che purtroppo non funziona) è questo:

Codice: [Seleziona]
package com.readingsensor;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.AppCompatButton;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.readingsensor.AlarmReceived;

public class MainActivity extends AppCompatActivity
{
    private AppCompatButton bottoneStartStop;
    private PendingIntent pendingIntent;
    private AlarmManager alarmManager;

    public static final int INTERVALLO_LETTURA_SENSORE = 1000 * 2;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceived.class);
        pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        bottoneStartStop = (AppCompatButton) findViewById(R.id.idBottoneStartStop);

        bottoneStartStop.setOnClickListener(new View.OnClickListener()
        {
            public void onClick(View v)
            {
                // Code here executes on main thread after user presses button
                if (bottoneStartStop.getText().equals("Start"))
                {
                    // ogni 5 secondi viene lanciato l'allarme
                    Log.i("MainActivity", "Ho premuto start");
                    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), INTERVALLO_LETTURA_SENSORE, pendingIntent);
                    Toast.makeText(getApplicationContext(), "Allarme lanciato tra 2 secondi", Toast.LENGTH_SHORT).show();
                    bottoneStartStop.setText(R.string.bottoneStop);
            }
                else
                {
                    Log.i("MainActivity", "Ho premuto stop");
                    alarmManager.cancel(pendingIntent);
                    Toast.makeText(getApplicationContext(), "Allarme terminato", Toast.LENGTH_SHORT).show();
                    bottoneStartStop.setText(R.string.bottoneStart);
                }
            }
        });
    }
}
Codice: [Seleziona]
package com.readingsensor;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

public class AlarmReceived extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Log.i("AlarmtReceived", "E' stato ricevuto l'allarme del timer. Leggo il sensore");
        Toast.makeText(context, "ALLARME RICEVUTO", Toast.LENGTH_SHORT).show();

    }
}



Praticamente alla pressione del tasto start, l'applicazione dovrebbe ogni 2 secondi mostrare un toast e scrivere un messaggio di log, ma non lo fa.
Dove sto sbagliando?
Grazie
« Ultima modifica: 27 Luglio 2017, 15:51:31 CEST da daniele087 »

Online iClaude

  • Utente normale
  • ***
  • Post: 323
  • Respect: +27
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S8
  • Sistema operativo:
    Windows 10
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #1 il: 27 Luglio 2017, 20:32:02 CEST »
0
Il codice è corretto, ma probabilmente lo stai testando su un dispositivo con un API level >= 19, dove la tempistica degli allarmi impostati con setRepeating non è più esatta. L'allarme funziona, ma non ogni due secondi: magari parte ogni minuto o ogni 2 minuti.
Per ottenere un effetto il più possibile simile a quello che vuoi tu con un AlarmManager devi usare:
- API fino a 18: il tuo codice va bene
- API 19-22: setExact con rischedulizzazione dell'allarme manuale (in pratica non appena ricevi il primo broadcast nel BroadcastReceiver, imposti un nuovo allarme fra 2 secondi)
- API >= 23: setExactAndAllowWhileIdle  con rischedulizzazione dell'allarme manuale.

In pratica con dispositivi recenti non otterrai mai una temporizzazione degli allarmi perfetta al secondo: ci saranno sempre delle piccole differenze.

Questo è un piccolo tutorial dove ho cercato di riassumere la situazione allo stato attuale: credo sia ancora aggiornato:
Android - The Technical Blog: Scheduling repeated tasks offline at (almost) exact times
« Ultima modifica: 27 Luglio 2017, 20:42:15 CEST da iClaude »

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #2 il: 27 Luglio 2017, 21:22:11 CEST »
0
Grazie.
Si, sapevo di questa cosa ma non era il problema.
Non so come mai ma c'era un problema del manifest e chissà perchè AS non me lo segnalava.

A parte questo, ti vorrei chiedere due cose:

1) secondo te l'approccio che sto seguendo, considerando che poi questa app verrà portata su smartwatch, è corretta? Si può fare di meglio?
2) al momento il mio codice fa si che più o meno ogni X secondi l'app gestisca l'allarme. Adesso dovrei fare in modo di leggere per (più o meno) Y secondi i dati di un sensore e pensavo di utilizzare la stessa tecnica, cioè di utilizzare un AlarmManager all'interno del metodo onRecive di AlarmReceived. Ma così facendo, una volta letti i dati come faccio a passarli all'activity per farglieli processare? Vorrei evitare di utilizzare sqlite perché alla fine non mi sembra il caso per un paio di valori per ogni misurazione del sensore. Inoltre, come so con certezza che se l'activity processa i dati, allora la lettura dal sensore è terminata?

Grazie

Online iClaude

  • Utente normale
  • ***
  • Post: 323
  • Respect: +27
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S8
  • Sistema operativo:
    Windows 10
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #3 il: 28 Luglio 2017, 10:48:44 CEST »
0
1) per il risparmio della batteria sarebbe preferibile utilizzare un JobScheduler (o FirebaseJobDispatcher), ma se la tua esigenza è quella di eseguire dei task a degli orari specifici non vedo altra soluzione che utilizzare un AlarmManager
2) le Activity sono utilizzate per gestire la UI, non per processare dati: per quello esistono i Service. Per memorizzare i dati puoi usare qualunque file, anche un semplice file di testo se ti basta

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #4 il: 28 Luglio 2017, 11:07:39 CEST »
0
Ma i service non funzionano esclusivamente a dispositivo acceso? A me serve che i dati vengano processati anche in standby.
In ogni caso l'idea di elaborare dati nell'Activity è solo temporanea, visto che devo mostrare una app di prova entro breve e quindi deve funzionare tutto anche se in modo non efficiente.
Pensavo quindi, a breve, di far eseguire le elaborazioni direttamente dentro la onSensorChanged implementato nel BroadcastReceiver utilizzato da AlarmManager visto che funziona anche in background.

Online iClaude

  • Utente normale
  • ***
  • Post: 323
  • Respect: +27
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S8
  • Sistema operativo:
    Windows 10
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #5 il: 28 Luglio 2017, 12:55:42 CEST »
0
Non è una buona idea lanciare thread o impostare listener di sensori in un BroadcastReceiver per una serie di ragioni (il SO può killare il BR in ogni momento, memory leaks, ecc.).
La soluzione migliore è lanciare un Service dal metodo onReceive ed eseguire nel Service tutte le operazioni volute.

Offline tonno16

  • Utente storico
  • *****
  • Post: 1233
  • Respect: +60
    • Mostra profilo
  • Dispositivo Android:
    moto g
  • Play Store ID:
    Diego Tonini
  • Sistema operativo:
    OpenSuse
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #6 il: 28 Luglio 2017, 18:53:35 CEST »
0
Quoto. Io avevo lo stesso problema. Usavo un broadcast receiver che lanciava un service.

Aggiungo anche che puoi settare un broadcast receiver per far si che al boot parta il service

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #7 il: 28 Luglio 2017, 19:05:52 CEST »
0
Ok grazie, vedrò di aggiustare il codice e vi farò sapere.
No comunque non mi interessa che parta al boot, perché deve essere l'utente a scegliere quando iniziare e terminare la lettura dei dati.

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #8 il: 16 Settembre 2017, 14:17:00 CEST »
0
Aggiorno la discussione.
Abbiamo parlato di Service, ma non sono sicuro sia la soluzione migliore per la mia app.
Quello che devo fare è leggere in background dei dati dal sensore, passarli poi all'activity che chiamerà una classe che li elaborerà e in base alla risposta che darà, la UI dell'activity si comporterà in un modo differente.

Il service però so che sia una buona soluzione se NON devo avere a che fare con lo UI thread e per operazioni lunghe, mentre la mia lettura dei dati prende pochi secondi e poi deve comunicare delle informazioni all'activity.

C'è qualcosa di meglio che posso usare?
Vedo online che ci sono diverse classi, ma non riesco a capire bene quale più si adatti alla mia situazione.

Grazie

Post unito: [time]16 Settembre 2017, 16:11:50 CEST[/time]
AsynkTask potrebbe fare al caso mio.

Post unito: 17 Settembre 2017, 13:44:29 CEST
Non è una buona idea lanciare thread o impostare listener di sensori in un BroadcastReceiver per una serie di ragioni (il SO può killare il BR in ogni momento, memory leaks, ecc.).
La soluzione migliore è lanciare un Service dal metodo onReceive ed eseguire nel Service tutte le operazioni volute.
Quindi nel Service (o IntentService) posso implementare il listener del sensore?
« Ultima modifica: 17 Settembre 2017, 13:44:29 CEST da daniele087, Reason: Merged DoublePost »

Online iClaude

  • Utente normale
  • ***
  • Post: 323
  • Respect: +27
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S8
  • Sistema operativo:
    Windows 10
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #9 il: 18 Settembre 2017, 20:32:24 CEST »
0
AsynkTask potrebbe fare al caso mio.

Quindi nel Service (o IntentService) posso implementare il listener del sensore?

Sì.
Cmq qua è spiegato perché è meglio non lanciare thread separati in un BR:
https://developer.android.com/guide/components/broadcasts.html#effects_on_process_state

Nella guida mettono anche un'altra opzione: usare goAsync() ma non so se può essere usata nel tuo caso. E cmq, anche in questo caso, devi terminare le operazioni nel BR in max 10 sec.
« Ultima modifica: 18 Settembre 2017, 20:50:37 CEST da iClaude »

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #10 il: 18 Settembre 2017, 20:55:46 CEST »
0
Grazie per la risposta.

Vorrei chiedere ancora qualche cosa:
1) una volta che il service (io ho usato IntentService) ha acquisito i valori che mi servono, come li posso passare all'Activity in modo che li mostri?
La strada più semplice sarebbe usare startActivity(), ma questo mi costringerebbe ad eseguire nuovamente la onCreate() e quindi mi porta alla seconda domanda.

2) Ho il seguente codice all'interno di onCreate():

Codice: [Seleziona]
protected void onCreate(Bundle savedInstanceState)
{
       super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);

      alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
      Intent alarmIntent = new Intent(getApplicationContext(), ActivityReceiver.class);
      pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);

     alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,  0, 5000, pendingIntent);

      // ...
}

Ogni volta che uso startActivity() viene eseguito il blocco di codice in onCreate(), ma così facendo non corro il rischio di avviare tanti allarmi?

3) Sempre riferito al codice del punto 2. Se malauguratamente l'activity dovesse essere eliminata (o dovesse essere laciato startActivity()), come posso fare a mantenere il riferimento all'allarme per cancellarlo?

Grazie

Online iClaude

  • Utente normale
  • ***
  • Post: 323
  • Respect: +27
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S8
  • Sistema operativo:
    Windows 10
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #11 il: 18 Settembre 2017, 20:58:56 CEST »
0
Grazie per la risposta.

Vorrei chiedere ancora qualche cosa:
1) una volta che il service (io ho usato IntentService) ha acquisito i valori che mi servono, come li posso passare all'Activity in modo che li mostri?
La strada più semplice sarebbe usare startActivity(), ma questo mi costringerebbe ad eseguire nuovamente la onCreate() e quindi mi porta alla seconda domanda.


Potresti inviare un broadcast che viene intercettato da un BroadcastReceiver registrato dalla tua Activity.

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #12 il: 19 Settembre 2017, 12:35:04 CEST »
0
Hai del codice di esempio?

Offline daniele087

  • Nuovo arrivato
  • *
  • Post: 26
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Honor 8
  • Sistema operativo:
    Debian GNU/Linux
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #13 il: 20 Settembre 2017, 23:28:04 CEST »
0
Hai del codice di esempio?
Risolto.

Ho un'altra domanda:
all'interno del service, devo effettuare una lettura di tot secondi.
Per ora ho implementato questa cosa con CountDownTimer.
In pratica prima all'interno di onHandleIntent() avvio il count down e quando il count down termina, nel suo metodo onFinish() invio in broadcast i valori letti.
Utilizzare CountDownTimer è una buona idea o posso usare altro?

Grazie
« Ultima modifica: 21 Settembre 2017, 00:49:27 CEST da daniele087 »

Online iClaude

  • Utente normale
  • ***
  • Post: 323
  • Respect: +27
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S8
  • Sistema operativo:
    Windows 10
Re:Esecuzione di misurazioni ogni X minuti per Y secondi
« Risposta #14 il: 21 Settembre 2017, 20:20:51 CEST »
0
all'interno del service, devo effettuare una lettura di tot secondi.
Per ora ho implementato questa cosa con CountDownTimer.
In pratica prima all'interno di onHandleIntent() avvio il count down e quando il count down termina, nel suo metodo onFinish() invio in broadcast i valori letti.
Utilizzare CountDownTimer è una buona idea o posso usare altro?

Grazie

Mi sembra una buona idea.
Se guardi il codice della classe CountDownTimer:
Cross Reference: /frameworks/base/core/java/android/os/CountDownTimer.java
vedrai che usa un Handler che invia dei msg con un certo delay. E' lo stesso sistema che useresti se lo programmassi manualmente. Ovviamente a te gli onTick non interessano, per cui puoi impostarli uguali al tempo totale.
« Ultima modifica: 21 Settembre 2017, 20:22:23 CEST da iClaude »