2013-07-12 2 views
6

Чтобы получить fused location в фоновом режиме, я создал библиотеку, которая очень похожа на библиотеку cwac-locpoll, созданную Commonsguy.LocationClient не дает обратного вызова, когда свет экрана гаснет, но мой WakefulThread работает безупречно, как ожидалось

Внутри PollerThread, я пытаюсь подключиться, запросить и получить данные, используя LocationClient.

Я могу подключиться, получив обратный вызов на onConnected метод, но я не в состоянии получить обратный вызов на onLocationChanged method.so мой onTimeout нить выполняет в соответствии с интервалом решил.

ПРИМЕЧАНИЕ:Эта проблема возникает только тогда, когда экран гаснет off.otherwise он работает совершенно нормально.

Я подозреваю, что может возникнуть ошибка в новом месте Api.

Вот реализация моих PollerThread,

  private class PollerThread extends WakefulThread implements GooglePlayServicesClient.ConnectionCallbacks, 
     GooglePlayServicesClient.OnConnectionFailedListener,LocationListener{ 

      private static final String TAG = "PollerThread"; 

      //context 
      private Context mContext=null; 
      private LocationClient mLocationClient=null; 
      private LocationRequest mLocationRequest=null; 
      private LocationManager locMgr=null; 
      private Intent intentTemplate=null; 
      private Handler handler=new Handler(); 
      private Runnable onTimeout = new Runnable() { 

      @Override 
      public void run() { 

      Log.e(TAG, "onTimeout"); 

      //prepare broadcast intent 
       Intent toBroadcast=new Intent(intentTemplate); 
       toBroadcast.putExtra(FusedPoller.EXTRA_ERROR, "Timeout!"); 
       toBroadcast.putExtra(
         FusedPoller.EXTRA_ERROR_PROVIDER_DISABLED, false); 
       toBroadcast.putExtra(FusedPoller.EXTRA_LASTKNOWN, 
         mLocationClient.getLastLocation()); 
       sendBroadcast(toBroadcast); 

      //stop the thread 
       quit(); 
      } 
      }; 

     PollerThread(Context mContext,LocationRequest mLocationRequest,PowerManager.WakeLock lock, LocationManager locMgr, 
        Intent intentTemplate) { 
      super(lock, "LocationPoller-PollerThread"); 

      Log.e(TAG, "PollerThread"); 

      this.mContext=mContext; 
      this.mLocationRequest=mLocationRequest; 
      this.locMgr=locMgr; 
      this.intentTemplate=intentTemplate; 
     } 


     @Override 
     protected void onPreExecute() { 
      super.onPreExecute(); 
       Log.e(TAG, "onPreExecute"); 

      //setup timeout 
      setTimeoutAlarm(); 

      //initiate connection 
      initiateConnection(); 
     } 

     @Override 
     protected void onPostExecute() { 
      super.onPostExecute(); 
      Log.e(TAG, "onPostExecute"); 

      //remove timeout 
      removeTimeoutAlarm(); 
      //disconnect 
      initiateDisconnection(); 
     } 

     /** 
     * Called when the WakeLock is completely unlocked. 
     * Stops the service, so everything shuts down. 
     */ 
     @Override 
     protected void onUnlocked() { 
      Log.e(TAG, "onUnlocked"); 
      stopSelf(); 
     } 


     private void setTimeoutAlarm() { 
      Log.e(TAG, "setTimeoutAlarm"); 
      handler.postDelayed(onTimeout, FusedLocationUtils.DEFAULT_TIMEOUT); 
     } 

     private void removeTimeoutAlarm() 
     { 
      Log.e(TAG, "removeTimeoutAlarm"); 
      handler.removeCallbacks(onTimeout); 
     } 

     private void initiateConnection() 
     { 
      Log.e(TAG, "initiateConnection"); 
      mLocationClient = new LocationClient(this.mContext, this, this); 
      mLocationClient.connect(); 
     } 

     private void initiateDisconnection() 
     { 
      Log.e(TAG, "initiateDisconnection"); 
      if(mLocationClient.isConnected()) 
      { 
       mLocationClient.disconnect(); 
      } 
     } 


     @Override 
     public void onConnected(Bundle arg0) { 
      Log.e(TAG, "onConnected"); 


      Log.e(TAG, "provider: GPS-"+locMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)+" NETWORK-"+locMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER)); 

       if (!(locMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)) && !(locMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) { 
        Log.e(TAG, "both disabled"); 

        //get last location and broadcast it 
        getLastLocationAndBroadcast(); 

        //stop the thread 
        quit(); 
       } 
       else 
       { 
        Log.e(TAG, "provider enabled"); 

        //get latest location and broadcast it 
        getLatestLocationAndBroadcast(); 
        //don't quit from here,quit from onLocationChanged 
       } 

     } 


     @Override 
     public void onDisconnected() { 
      Log.e(TAG, "onDisconnected"); 
      // TODO Auto-generated method stub 

     } 

     @Override 
     public void onConnectionFailed(ConnectionResult arg0) { 
      Log.e(TAG, "onConnectionFailed"); 
      // TODO Auto-generated method stub 

     } 
     @Override 
     public void onLocationChanged(Location location) { 


      Log.e(TAG, "onLocationChanged"); 

      //prepare broadcast intent 
      Intent toBroadcast=new Intent(intentTemplate); 
      toBroadcast.putExtra(FusedPoller.EXTRA_LOCATION, location); 
      sendBroadcast(toBroadcast); 

      //stop further updates 
      stopUpdates(); 
      //stop the thread 
      quit(); 

     } 

     private void getLatestLocationAndBroadcast() { 
      Log.e(TAG, "getLatestLocationAndBroadcast"); 
      if(mLocationClient.isConnected() && servicesConnected()) 
      { 
       Log.e(TAG, "going to request updates"); 
       Log.e(TAG, "lockStatic.isHeld(): "+lockStatic.isHeld()); 
       mLocationClient.requestLocationUpdates(mLocationRequest, this); 
      } 
      else 
      { 
       Log.e(TAG, "not going to request updates"); 
      } 
     } 


     private void stopUpdates() { 
      Log.e(TAG, "stopUpdates"); 
      if(servicesConnected()) 
      { 
       Log.e(TAG,getString(R.string.location_updates_stopped)); 
       mLocationClient.removeLocationUpdates(this); 
      } 
      else 
      { 
       Log.e(TAG,"can't do:"+getString(R.string.location_updates_stopped)); 
      } 
     } 



     private void getLastLocationAndBroadcast() { 
      Log.e(TAG, "getLastLocationAndBroadcast"); 
      if(mLocationClient.isConnected() && servicesConnected()) 
      { 
       Log.e(TAG, "going to get last location: "+mLocationClient.getLastLocation()); 

       Intent toBroadcast = new Intent(intentTemplate); 
       toBroadcast.putExtra(FusedPoller.EXTRA_ERROR, 
         "Location Provider disabled!"); 
       toBroadcast.putExtra(
         FusedPoller.EXTRA_ERROR_PROVIDER_DISABLED, true); 
       toBroadcast.putExtra(FusedPoller.EXTRA_LASTKNOWN, 
         mLocationClient.getLastLocation()); 
       sendBroadcast(toBroadcast); 
      } 
      else 
      { 
       Log.e(TAG, "not going to get last location"); 
      } 
     } 
     } 

и servicesConnected реализация метода,

  /** 
     * Verify that Google Play services is available before making a request. 
     * 
     * @return true if Google Play services is available, otherwise false 
     */ 
     private boolean servicesConnected() { 

       Log.e(TAG, "servicesConnected"); 

      // Check that Google Play services is available 
      int resultCode = 
        GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); 

      // If Google Play services is available 
      if (ConnectionResult.SUCCESS == resultCode) { 
       // In debug mode, log the status 
       Log.d(FusedLocationUtils.APPTAG, getString(R.string.play_services_available)); 

       // Continue 
       return true; 
      // Google Play services was not available for some reason 
      } else { 
       // Display an error dialog 
       Log.d(FusedLocationUtils.APPTAG, getString(R.string.play_services_unavailable)); 
       Toast.makeText(this, getString(R.string.play_services_unavailable), Toast.LENGTH_SHORT).show(); 

       return false; 
      } 
     } 
+0

Вы уверены, что вы держите в руках 'WakeLock'? – CommonsWare

+0

@CommonsWare: Да, я уверен, поскольку я напечатал журнал внутри метода getLatestLocationAndBroadcast, чтобы узнать статус 'lockStatic.isHeld()'. И он дает положительный ответ. –

+0

Не знаете, что вам сказать, извините. – CommonsWare

ответ

17

Если вы хотите слушать частые обновления местоположения в фоновом режиме (например, каждый второй), вы должны использовать код в пределах Service:

http://developer.android.com/reference/android/app/Service.html

Деятельность может быть завершена платформой Android в любой момент времени, когда они не находятся на переднем плане.

При использовании Сервиса я бы рекомендовал, чтобы Служба реализовала LocationListener напрямую, а не Thread внутри Сервиса. Например, использование:

public class LocListener extends Service implements com.google.android.gms.location.LocationListener, ...{ 

Я использовал эту конструкцию реализации LocationListener непосредственно на службе с LocationClient и слитым поставщиком местоположения в моем GPS Benchmark app, и я могу подтвердить, что это работает, даже когда экран выключен и приложение работает в фоновом режиме.

Если вы хотите слушать случайные геоданных в фоновом режиме (например, каждую минуту), используя слитый поставщика местоположения, лучше дизайн использовать PendingIntents, используя LocationClient.requestLocationUpdates(Location Request, PendingIntent callbackIntent) метод:

https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html#requestLocationUpdates(com.google.android.gms.location.LocationRequest,%20android.app.PendingIntent)

Из приведенного выше документа Android:

Этот метод подходит для использования в фоновом режиме, особенно для получения обновлений местоположения, даже когда приложение было убито системой. Чтобы сделать это, используйте PendingIntent для запущенной службы. Для случаев использования переднего плана рекомендуется использовать версию LocationListener метода, см. RequestLocationUpdates (LocationRequest, LocationListener).

Любое предыдущее LocationRequests, зарегистрированное на этом PendingIntent, будет заменено.

Обновления местоположения отправляются с ключом KEY_LOCATION_CHANGED и значением местоположения в намерении.

Смотрите пример активность Recognition для более детального описания использования PendingIntents для получения обновлений во время работы в фоновом режиме:

https://developer.android.com/training/location/activity-recognition.html

Модифицированные выдержки из этой документации ниже, изменены мной, чтобы быть специфические для обновлений местоположения.

Сначала объявить Намерение:

public class MainActivity extends FragmentActivity implements 
     ConnectionCallbacks, OnConnectionFailedListener { 
    ... 
    ... 
    /* 
    * Store the PendingIntent used to send location updates 
    * back to the app 
    */ 
    private PendingIntent mLocationPendingIntent; 
    // Store the current location client 
    private LocationClient mLocationClient; 
    ... 
} 

обновления запроса, как вы в настоящее время, но на этот раз проходят в ожидании намерения:

/* 
* Create the PendingIntent that Location Services uses 
* to send location updates back to this app. 
*/ 
Intent intent = new Intent(
     mContext, LocationIntentService.class); 

... 
//Set up LocationRequest with desired parameter here 
... 
/* 
* Request a PendingIntent that starts the IntentService. 
*/ 
mLocationPendingIntent = 
     PendingIntent.getService(mContext, 0, intent, 
     PendingIntent.FLAG_UPDATE_CURRENT); 
/* 
* Request location updates 
*/ 
mLocationClient.requestLocationUpdates(mLocationRequest, callbackIntent); 

Ручка Location Updates

Для обработки Предписание, которое службы местоположения отправляют для каждого интервала обновления, определяют IntentService и его необходимый метод onHandleIntent(). Службы определения местоположения отправляют ... обновления как объекты Intent, используя PendingIntent, который вы указали при вызове requestLocationUpdates(). Поскольку вы предоставили явное намерение для PendingIntent, единственным компонентом, который получает намерение, является IntentService, который вы определяете.

Определить класс и необходимый метод onHandleIntent():

/** 
* Service that receives Location updates. It receives 
* updates in the background, even if the main Activity is not visible. 
*/ 
public class LocationIntentService extends IntentService { 
    ... 
    /** 
    * Called when a new location update is available. 
    */ 
    @Override 
    protected void onHandleIntent(Intent intent) { 
     Bundle b = intent.getExtras(); 
     Location loc = (Location) b.get(LocationClient.KEY_LOCATION_CHANGED); 
     Log.d(TAG, "Updated location: " + loc.toString()); 


    } 
    ... 
} 

ВАЖНО - быть как можно более эффективным, ваш код в onHandleIntent() должен вернуться как можно быстрее, чтобы позволить IntentService закрыть , Из IntentService Документов

http://developer.android.com/reference/android/app/IntentService.html#onHandleIntent(android.content.Intent)

Этот метод вызывается на рабочем потоке с просьбой обработать. За один раз обрабатывается только один Intent, но обработка происходит в рабочем потоке, который выполняется независимо от другой логики приложения. Таким образом, если этот код занимает много времени, он будет задерживать другие запросы к одному и тому же IntentService, но он ничего не задержит. Когда все запросы обрабатываются, IntentService останавливается, поэтому вы не должны называть stopSelf().

Мое понимание IntentService конструкции является то, что вы можете икру темы внутри onHandleIntent(), чтобы избежать блокирования других обновлений местоположения через платформу вызовы onHandleIntent(), просто надо знать, что служба будет продолжать работать, пока все бегущие нити прекращается.

+0

Я использовал 'Сервис', и я использую' LocationListener' также. Вы можете взглянуть на эту реализацию 'https://github.com/commonsguy/cwac-locpoll/blob/master/src/com/commonsware/cwac/locpoll/LocationPollerService.java', Как указано в вопросе Я пытаюсь создать подобную библиотеку, и я реализовал 'PollerThread' по-другому в соответствии с моим требованием из-за нового API Location. –

+3

LocationClient был реализован с целью отвлечения разработчиков от управления своими собственными wakelocks и службами в фоновом режиме, чтобы сделать отслеживание местоположения более эффективным. Идея поставщика плавного местоположения заключается в том, что приложение должно указывать общий баланс для точности/энергии, а затем пусть плавный провайдер позаботится о низкоуровневом управлении процессом, чтобы получить образцы. Приложению нужно только прослушивать обновления. Если команда Android не говорит иначе, эта «ошибка» может быть спроектирована или, по крайней мере, не областью, в которой они сильно тестировались, ожидая, что разработчики будут использовать PendingIntents. –

+0

Я согласен с идеей провайдера плавного местоположения, но в случае отслеживания местоположений в режиме «background» разработчики должны писать службы для получения частых обновлений, и эта служба должна содержать wakelock, чтобы поддерживать процессор awake.so, в конечном счете, разработчики чтобы управлять этими вещами самостоятельно. И это то, что я делаю. Короче говоря, неприемлемым поведением является то, что новый API Fused Location API может предоставлять частые обновления местоположения только в режиме «переднего плана». –

0

Я потратил несколько дней, пытаясь получить WiFi и ячейки с заблокированным экраном с Android 6.0 на Nexus 6. И похоже, что родной сервис определения местоположения Android не позволяет это делать. После того, как устройство заблокировано, он по-прежнему собирает события обновления местоположения в течение 10-15 минут, а затем останавливается, чтобы обеспечить любые обновления местоположения.

В моем случае решение заключалось в том, чтобы перейти от службы определения местоположения на основе Android к обертке Google Play Services, называемой com.google.android.gms.Местоположение: https://developers.google.com/android/reference/com/google/android/gms/location/package-summary

Да, я знаю, что на некоторых устройствах Android отсутствует GMS, но для моего приложения это единственное решение для выполнения.

Он не останавливает отправку обновлений местоположения, даже если в фоновом режиме и экран устройства заблокирован.

Меня лично предпочитаю библиотеку RxJava, чтобы обернуть эту услугу в потоке (примеры включены): https://github.com/mcharmas/Android-ReactiveLocation