5

我遇到問題,經過一些搜索後,我還沒有找到任何正面解決方案。 經過研究,我有想法,沒有實現我的問題,但這個問題可能是我的最後一次機會。Android,在設備處於睡眠模式時獲取強度信號(PhoneStateListener)

我需要什麼?

有應用程序獲取有關移動網絡強度信號的信息。我做到了 PhoneStateListener。當然,它的偉大工程,但是當我的設備進入睡眠模式,聽者不工作:

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

WakeLock只有在情況下解決問題,如果設備關閉的超時。如果按下硬電源按鈕,我的設備也會進入睡眠模式。我們無法覆蓋電源按鈕操作。

我的目標是始終在設備啓用時獲得強度信號。不管什麼模式。所有時間都應該收集數據。

問:

有什麼想法?如何實現這一目標?有沒有辦法做到這一點或可能有一些黑客?歡迎所有解決方案。如果你有一些有用的經驗,請分享一下。

感謝大家的幫助!我希望這個話題能夠得到關於這個問題的完整信息。

+1

「如果我用力按壓電源鍵,我的設備獲得的休眠模式以及我們不能忽略電源按鈕的動作。」 - 「WakeLock」不受電源按鈕的影響。 – CommonsWare

+0

爲什麼不使用服務? –

+0

它在睡眠模式下也不起作用。 –

回答

9

報警經理是要走的路 - 棘手的部分是要保持電話報警經理接收器返回後清醒。所以

  • 設置一個報警(請注意,你也應該註冊一個「引導完成」接收器設置在重啓後報警 - 你報警沒有生存重啓):

    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); 
    
  • 接受它 - 接收器在其onReceive(),從來沒有失敗持有喚醒鎖定:

    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; 
         } 
        } 
    } 
    

    如果你是幸運的(yourRetrieveSignal()是足夠快),這將工作,OTH否則,您需要在接收器中使用(喚醒)IntentService模式。
    WakefulIntentService將照顧喚醒鎖(如果你想避免依賴看看here) - 編輯:請記住,你不能在意圖服務中定義監聽者 - 請參閱here

如果接收者ANR在你身上,你必須嘗試WakefulIntentService模式。在這兩種情況下,你可以使用this

這實際上證明是最困難的部分:

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; 
    } 
} 

其中:

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; 
    } 
} 

這是工作的代碼(但不承認優雅的巔峯之作 - 意見/更正歡迎)。不要忘記在清單中註冊您的服務並獲取權限。
編輯2013年7月23日:我沒有使用onReceive - 如果你使用它它會ANR - 如果你在onReceive使用WakefulIntentService,並在那裏你調用SignalListenerService這是工作的代碼。

+1

如果您只支持Android 2.1及更高版本,則需要使用「PhoneStateListener.LISTEN_SIGNAL_STRENGTHS」而不是「PhoneStateListener.LISTEN_SIGNAL_STRENGTH」。 – ChuongPham

1

根據我對PhoneStateListener的理解,當應用程序CPU處於睡眠模式時,您無法執行此操作。您可以讓設備保持清醒狀態,這會損壞電池壽命。或者,您可以使用警報(請參閱AlarmManager)定期喚醒設備,以便收集數據(仍會影響電池壽命)。

Some samples of using AlarmManager can be found here

+0

如何讓設備保持清醒?按下硬件電源按鈕後,我的設備無論如何都會進入睡眠模式。或者可能我能以某種方式實現這一目標?主要問題是電源按鈕的情況。 –

+0

然後它聽起來像你想使用AlarmManager。 :) AlarmManger將允許您喚醒CPU並每X分鐘/秒執行一段代碼。 – AndersNS

+0

如果您使用AlarmManager,您仍然會影響電池電量。我猜如果你要在你的應用中實現這個功能,你需要向用戶解釋他們的電池可能會耗盡一點。 – ChuongPham

0

Android問題10931可能的解決方法之一是將android.intent.action.SCREEN_ON意圖發送到屏幕關閉後的「手機」進程。

  1. 創建並註冊BroadcastReceiver偵聽通知的屏幕關閉

    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. 發送SCREEN_ON意圖手機只處理的時候。

    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(); 
         } 
        } 
    }; 
    

收到此意圖在手機進程將繼續發送單元位置 更新之後。

需要root權限。

該解決方案是一個有點哈克,危險的,不適用所有手機。它可能會導致更高的功耗,但不會比打開屏幕更多。

相關問題