5

Ho un problema e dopo qualche ricerca non ho trovato nessuna soluzione positiva. Dopo la ricerca ho idea che non c'è implementazione per il mio problema ma questa domanda potrebbe essere la mia ultima possibilità.Android, riceve segnale di forza (PhoneStateListener) mentre il dispositivo è in modalità sleep

Cosa devo ottenere?

C'è un'applicazione che ottiene informazioni sul segnale di intensità della rete mobile. Lo faccio da PhoneStateListener. Naturalmente funziona benissimo ma quando il mio dispositivo entra in modalità sleep, ascoltatore non funziona:

https://code.google.com/p/android/issues/detail?id=10931 https://code.google.com/p/android/issues/detail?id=7592

WakeLock risolve il problema solo in caso, se il dispositivo si spegne per timeout. Nel caso in cui quando premo il pulsante di accensione intensa, anche il mio dispositivo ottiene la modalità di sospensione. Non possiamo ignorare l'azione del pulsante di accensione.

Il mio obiettivo è ottenere il segnale di forza sempre quando il mio dispositivo è abilitato. Non importa quale sia la modalità. Dovrebbe sempre raccogliere dati.

Domanda:

Esistono idee? Come raggiungerlo? Ci sono modi per farlo o potrebbero esserci degli hack? Tutti i risolti sono i benvenuti. Se hai avuto qualche esperienza utile, per favore condividi questo.

Grazie a tutti per l'aiuto !!! Spero che questo argomento ottenga informazioni complete su questo problema.

+1

"Nel caso in cui premo il pulsante di accensione, il mio dispositivo si attiva anche nella modalità di sospensione. Non è possibile ignorare l'azione del pulsante di accensione." - un 'WakeLock' non è influenzato dal pulsante di accensione. – CommonsWare

+0

Perché non utilizzare un servizio? –

+0

non funziona anche in modalità sleep. –

risposta

9

direttore allarme è la strada da percorrere - la parte difficile è quello di tenere il telefono svegli dopo che i rendimenti del ricevitore Alarm Manager. Così

  • impostazione di un allarme (avviso si dovrebbe anche registrare un ricevitore "On Boot completato" per impostare la sveglia dopo un riavvio - vostri allarmi non sopravvivono un riavvio):

    Intent monitoringIntent = new Intent(context, YourReceiver.class); 
    monitoringIntent.setAction("your action"); 
    PendingIntent pi = PendingIntent.getBroadcast(context, NOT_USED, 
             monitoringIntent, PendingIntent.FLAG_UPDATE_CURRENT); 
    AlarmManager am = (AlarmManager) 
            context.getSystemService(Context.ALARM_SERVICE); 
    // here is the alarm set up 
    am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
           SystemClock.elapsedRealtime() + INITIAL_DELAY, 
           INTERVAL_BETWEEN_ALARMS, pi); 
    
  • riceverla - il ricevitore tiene un Wakelock nella sua onReceive() che non manca mai:

    public abstract class YourReceiver extends BroadcastReceiver { 
    
        @Override 
        final public void onReceive(Context context, Intent intent) { 
         final String action = intent.getAction(); 
         if ("your action".equals(action)) { 
          // monitoring - got broadcast from ALARM 
          try { 
            d("SS : " + new Signal().getSignalStrength(context)); 
          } catch (InterruptedException e) { 
            e.printStackTrace(); 
          } 
          // Actu8ally the lines above will ANR 
          // I did it with WakefulIntentService : 
          // WakefulIntentService.sendWakefulWork(
          // context, YourWakefulService.class); 
          // Will be posting it asap 
         } else { 
          w("Received bogus intent : " + intent); 
          return; 
         } 
        } 
    } 
    

    Se siete fortunati (yourRetrieveSignal() è abbastanza veloce) questo lavoro, OTH erwise avrai bisogno di un pattern (Wakeful) IntentService nel tuo ricevitore.
    Il WakefulIntentService si prenderà cura del blocco della sveglia (se si desidera evitare una dipendenza, dare un'occhiata a here) - MODIFICA: tenere presente che non è possibile definire gli ascoltatori in un servizio intent - vedere here.

Se le ANR ricevitore su di voi, dovete provare il modello WakefulIntentService.In entrambi i casi è possibile utilizzare this:

questo si è rivelato la parte più difficile in realtà:

class Signal { 

    static volatile CountDownLatch latch; //volatile is an overkill quite probably 
    static int asu; 
    private final static String TAG = Signal.class.getName(); 

    int getSignalStrength(Context ctx) throws InterruptedException { 
     Intent i = new Intent(TAG + ".SIGNAL_ACTION", Uri.EMPTY, ctx, 
       SignalListenerService.class); 
     latch = new CountDownLatch(1); 
     asu = -1; 
     ctx.startService(i); 
     Log.d(TAG, "I wait"); 
     latch.await(); 
     ctx.stopService(i); 
     return asu; 
    } 
} 

dove:

public class SignalListenerService extends Service { 

    private TelephonyManager Tel; 
    private SignalListener listener; 
    private final static String TAG = SignalListenerService.class.getName(); 

    private static class SignalListener extends PhoneStateListener { 

     private volatile CountDownLatch latch; 

     private SignalListener(CountDownLatch la) { 
      Log.w(this.getClass().getName(), "CSTOR"); 
      this.latch = la; 
     } 

     @Override 
     public void onSignalStrengthChanged(int asu) { 
      Signal.asu = asu; 
      latch.countDown(); 
     } 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     Log.w(TAG, "Received : " + intent.getAction()); 
     Tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 
     listener = new SignalListener(Signal.latch); 
     @SuppressWarnings("deprecation") 
     final int listenSs = PhoneStateListener.LISTEN_SIGNAL_STRENGTH; 
     Tel.listen(listener, listenSs); 
     return START_STICKY; 
    } 

    @Override 
    public void onDestroy() { 
     Log.w(TAG, "onDestroy"); 
     Tel.listen(listener, PhoneStateListener.LISTEN_NONE); 
     super.onDestroy(); 
    } 

    @Override 
    public IBinder onBind(Intent intent) { 
     return null; 
    } 
} 

Questo è il codice di lavoro (ma non l'apice di eleganza è vero - commenti/correzioni benvenuto). Non dimenticare di registrare i tuoi servizi nel manifest e acquisire le autorizzazioni.
EDIT 2013/07/23: non ho usato il onReceive - se lo si utilizza lo farà ANR - questo codice funziona se si utilizza un WakefulIntentService in onReceive e lì dentro si chiama SignalListenerService.

+1

Se supporti solo Android 2.1 e versioni successive, dovrai utilizzare 'PhoneStateListener.LISTEN_SIGNAL_STRENGTHS' anziché' PhoneStateListener.LISTEN_SIGNAL_STRENGTH'. – ChuongPham

1

Dalla mia comprensione di PhoneStateListener non è possibile farlo mentre la CPU dell'applicazione è in modalità sospensione. È possibile mantenere il dispositivo sveglio, il che potrebbe rovinare la durata della batteria. In alternativa, è possibile utilizzare un allarme (vedere AlarmManager) per riattivare il dispositivo a intervalli, in modo da poter raccogliere i dati (la durata della batteria è compromessa).

Some samples of using AlarmManager can be found here

+0

Come posso mantenere il dispositivo sveglio? Dopo aver premuto il pulsante di accensione dell'hardware, il mio dispositivo entra in modalità sospensione in ogni caso. O forse posso riuscirci in qualche modo? Il problema principale è il caso con il pulsante di accensione. –

+0

Sembra che tu voglia usare AlarmManager. :) AlarmManger ti permetterà di riattivare la CPU ed eseguire un pezzo di codice ogni X minuti/secondi. – AndersNS

+0

Se si utilizza AlarmManager, il consumo della batteria si ripercuote comunque. Immagino che se dovessi implementarlo nella tua app, dovrai spiegare agli utenti che la batteria potrebbe scaricarsi un po '. – ChuongPham

0

Una delle soluzioni possibili del problema Android 10931 è di inviare l'intento android.intent.action.SCREEN_ON al processo "telefono" dopo lo spegnimento dello schermo.

  1. Creare e registrare BroadcastReceiver ad ascoltare per le notifiche quando lo schermo si spegne

    start(Context context) { 
        IntentFilter filter = new IntentFilter(); 
        filter.addAction(Intent.ACTION_SCREEN_OFF); 
        context.registerReceiver(mScreenReceiver, filter); 
    } 
    
    final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { 
        @Override 
        public void onReceive(final Context context, final Intent intent) { 
         if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 
          Log.v(LOGTAG, "Screen is off. Running workaround"); 
          new Thread(mReportScreenIsOnRunnable).start(); 
         } 
        } 
    }; 
    
  2. Inviare il solo il processo di telefono SCREEN_ON intento di.

    public final Runnable mReportScreenIsOnRunnable = new Runnable() { 
        @Override 
        public void run() { 
         try { 
          Thread.sleep(100); 
         } catch (InterruptedException e) { 
          e.printStackTrace(); 
         } 
         try { 
          Runtime.getRuntime().exec(new String[] { "su", "-c", 
            "am broadcast -a android.intent.action.SCREEN_ON com.android.phone" }); 
         } catch (IOException e) { 
          e.printStackTrace(); 
         } 
        } 
    }; 
    

Dopo aver ricevuto questo intento il processo di telefono sarebbe riprendere l'invio posizione di cella aggiornamenti.

Sono richiesti i privilegi di root.

Questa soluzione è un po 'hacky, pericolosa e non funziona su tutti i telefoni. Può portare a un maggiore consumo energetico, ma non molto più che se si mantiene lo schermo acceso.