2011-04-25 14 views
37

He estado atascado en este problema por bastante tiempo. Estoy trabajando en una aplicación que usa la ubicación bastante extensamente en varias actividades diferentes. Cada ejemplo que he encontrado utiliza un LocationListener por separado en cada actividad. Esto no es factible en mi situación.Android: la mejor forma de implementar LocationListener en múltiples actividades

Me pregunto cuál es la forma más eficiente de realizar un seguimiento de la ubicación del usuario en varias actividades. En este momento, he creado un servicio que implementa LocationListener y usa una transmisión para actualizar latitud estática y campos largos en una clase base de actividad. Esto significa que el servicio siempre se está ejecutando, lo que no es ideal. Pero si apago el servicio y lo reinicio solo cuando lo necesito, toma tiempo obtener una buena ubicación. Necesito los datos de ubicación en Activity onCreate(). Es lo mismo si trato de implementarlo en la clase base de la actividad. Si registro constantemente el oyente en onResume/onCreate y lo anulo en onPause(), lleva demasiado tiempo comenzar a recibir actualizaciones. También traté de crear un servicio al que pudiera vincularme, por lo que solo comienza cuando necesito una ubicación. Pero tengo el mismo problema, lleva demasiado tiempo enlazar al servicio y comenzar a recibir actualizaciones.

El servicio que estoy utilizando ahora funciona, pero de todo lo que he leído, no debería estar utilizando un servicio constantemente en funcionamiento para cosas triviales como esta. Pero el objetivo de la aplicación es proporcionar datos relevantes basados ​​en la ubicación actual del usuario. Así que tengo un servicio que se ejecuta en segundo plano y proporciona actualizaciones periódicamente. El principal problema que me ha llevado a reexaminar el diseño en este momento es que recientemente descubrí que onProviderEnabled() no se llama si el usuario inicia la aplicación sin que el GPS esté encendido y luego lo habilita. En este escenario, la aplicación no tiene forma de reconocer que el GPS estaba habilitado para que pueda comenzar a escuchar las actualizaciones.

Pensé que entendía que LocationManager y LocationListener miraban los ejemplos, pero parece que no puedo aplicarlo a esta situación en la que necesito datos de ubicación en múltiples actividades. Cualquier ayuda o consejo sería muy apreciado.

Respuesta

43

La forma en que normalmente implementaría este requisito es mediante el uso de una implementación del Servicio enlazado, como la que se encuentra en el Local Service Sample en la Documentación del SDK. Obviamente, está familiarizado con la ventaja del Servicio que le permite crear todo el código de ubicación solo una vez.

El acceso al servicio a través de enlaces permite que el servicio se inicie y se detenga, por lo que no se ejecuta cuando su aplicación no está en primer plano (morirá tan pronto como no haya más actividades vinculadas). La clave, IMO, para hacer que esto funcione bien es VINCULAR el servicio en onStart() y DESVINCULAR en onStop(), porque esas dos llamadas se superponen al pasar de una actividad a otra (la segunda actividad comienza antes de que la primera se detenga). Esto evita que el Servicio muera cuando se mueve dentro de la aplicación, y solo deja que el servicio muera cuando toda la aplicación (o al menos cualquier parte interesada en la ubicación) abandona el primer plano.

Con enlaces, no tiene que pasar los datos de ubicación en una difusión, porque la actividad puede llamar a los métodos directamente en el servicio para obtener la última ubicación. Sin embargo, una Transmisión aún sería una ventaja como método para indicar CUÁNDO está disponible una nueva actualización ... pero esto se convertiría en un notificador a la Actividad de escucha para llamar al método getLocation() en el Servicio.

Mi $ 0.02. ¡Espero que ayude!

+0

He estado en este camino antes, pero no sabía que onStart y onStop se superpusieron así. Esto es atractivo para mí y lo probaré y les haré saber lo que se me ocurrirá mañana. ¡Gracias! – d370urn3ur

+1

Solo me preguntaba, ¿cómo funcionaría esto al apagar la pantalla? ¿Sería prudente vincular el servicio en onResume() y UNBIND en onPause() para activarlo/desactivarlo cuando la pantalla se enciende/apaga y funciona cuando las actividades cambian? –

+2

@Grantland Punto válido, ya que la Actividad sigue en primer plano, el Servicio viviría. Si el servicio necesita acelerarse en estas instancias, yo mantendría el enlace igual, pero tendré que registrar un 'BroadcastReceiver' para' Intent.ACTION_SCREEN_ON' y 'Intent.ACTION_SCREEN_OFF' para apagar los componentes pesados ​​como el GPS. Mover el mecanismo de enlace a onPause()/onResume() hace que el servicio posiblemente se detenga/reinicie a menudo durante el uso de la aplicación, ya que esas devoluciones de llamada nunca se superponen, lo que permite que el servicio caiga en un estado desacoplado con frecuencia. – Devunwired

0

Puede solicitar que su servicio escriba sus long coords lat & en un sqlite db al cambiar de esa manera no tiene la sobrecarga del servicio vinculante y sus coords estarán accesibles en todas sus actividades.

Quizás en su actividad principal pueda verificar el estado del GPS y, si está apagado, puede solicitarle al usuario que lo habilite. Una vez que el GPS está habilitado, inicie su servicio.

14

Tuve el mismo problema y traté de resolverlo con la buena respuesta de Devunwired, pero tuve algunos problemas. No pude encontrar una manera de detener el servicio y cuando terminé mi aplicación, el módulo GPS aún se estaba ejecutando. Así que me encontré otra manera:

escribí una clase GPS.java:

public class GPS { 

    private IGPSActivity main; 

    // Helper for GPS-Position 
    private LocationListener mlocListener; 
    private LocationManager mlocManager; 

    private boolean isRunning; 

    public GPS(IGPSActivity main) { 
     this.main = main; 

     // GPS Position 
     mlocManager = (LocationManager) ((Activity) this.main).getSystemService(Context.LOCATION_SERVICE); 
     mlocListener = new MyLocationListener(); 
     mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener); 
     // GPS Position END 
     this.isRunning = true; 
    } 

    public void stopGPS() { 
     if(isRunning) { 
      mlocManager.removeUpdates(mlocListener); 
      this.isRunning = false; 
     } 
    } 

    public void resumeGPS() { 
     mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener); 
     this.isRunning = true; 
    } 

    public boolean isRunning() { 
     return this.isRunning; 
    } 

    public class MyLocationListener implements LocationListener { 

     private final String TAG = MyLocationListener.class.getSimpleName(); 

     @Override 
     public void onLocationChanged(Location loc) { 
      GPS.this.main.locationChanged(loc.getLongitude(), loc.getLatitude()); 
     } 

     @Override 
     public void onProviderDisabled(String provider) { 
      GPS.this.main.displayGPSSettingsDialog(); 
     } 

     @Override 
     public void onProviderEnabled(String provider) { 

     } 

     @Override 
     public void onStatusChanged(String provider, int status, Bundle extras) { 

     } 

    } 

} 

Esta clase se utiliza en todas las actividades que necesita las coordenadas GPS. Cada actividad tiene que implementar la siguiente interfaz (necesario para la comunicación):

public interface IGPSActivity { 
    public void locationChanged(double longitude, double latitude); 
    public void displayGPSSettingsDialog(); 
} 

Ahora mi principal actividad se ve así:

public class MainActivity extends Activity implements IGPSActivity { 

    private GPS gps; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     gps = new GPS(this); 
    } 


    @Override 
    protected void onResume() { 
     if(!gps.isRunning()) gps.resumeGPS(); 
     super.onResume(); 
    } 

    @Override 
    protected void onStop() { 
     gps.stopGPS(); 
     super.onStop(); 
    } 


    public void locationChanged(double longitude, double latitude) { 
     Log.d(TAG, "Main-Longitude: " + longitude); 
     Log.d(TAG, "Main-Latitude: " + latitude); 
    } 


    @Override 
    public void displayGPSSettingsDialog() { 
       Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 
       startActivity(intent); 
    } 
} 

y un segundo uno así:

public class TEST4GPS extends Activity implements IGPSActivity{ 

    private GPS gps; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     this.gps = new GPS(this); 
    } 

    @Override 
    public void locationChanged(double longitude, double latitude) { 
     Log.d("TEST", "Test-Longitude: " + longitude); 
     Log.d("TEST", "Test-Latitude: " + latitude); 

    } 

    @Override 
    protected void onResume() { 
     if(!gps.isRunning()) gps.resumeGPS(); 
     super. onResume(); 
    } 

    @Override 
    protected void onStop() { 
     gps.stopGPS(); 
     super.onStop(); 
    } 

    @Override 
    public void displayGPSSettingsDialog() { 
       Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 
       startActivity(intent); 

    } 
} 

No es tan hermoso como la solución de Devunwired, pero funciona para mí. cya

+0

¡Me encantó tu solución! ¡Hice algunas mejoras y funcionó bien! Gracias. –

+0

@Spipau, me gusta su respuesta aquí. Lo intenté, pero no funciona para mí. ¿Alguna posibilidad de que puedas echarle un vistazo a mi código y ver si puedes descubrir lo que hice mal? http://stackoverflow.com/questions/29771717/android-gps-not-working-for-me – MrGibbage

+2

@MrGibbage Lo siento, pero tendría que volver a crear todo. Mi respuesta ahora es de 3 años y si tuviera que implementar una señal GPS global en mi aplicación otra vez, se vería completamente diferente.Coloque el código de seguimiento en su Aplicación y use el LocalBroadcastManager para enviar la posición a las Actividades/Fragmentos que elija. https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html – Spipau

-1

Esta es una manera muy simple y fácil de hacerlo:

en su actividad principal:

private boolean mFinish; 

@Override 
public void onBackPressed() { 
    mFinish = true; 
} 


@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    mFinish = false; 
    ... 
} 

@Override 
public void onPause(){ 
    super.onPause(); 
    if (mFinish) { 
     // remove updates here 
     mLocationManager.removeUpdates(mLocationListener); 
    } 
} 

Esto sólo se eliminará el actualizaciones oyente si pulsa Atrás de su actividad principal, pero de lo contrario, permanece encendido.

No es la mejor manera de hacerlo, pero es una solución simple de copiar y pegar.

Asegúrese de no llamar a escucha de ubicación si ya se está ejecutando (por ejemplo, en rotación de pantalla), de lo contrario terminará con múltiples oyentes ejecutándose en segundo plano.

+0

Downvoters: ¿le importa agregar una explicación de por qué? – lenooh

Cuestiones relacionadas