title: Android - How do I get location updates in the background in a service
body: <p>I need to access location in the background when my app is running.</p>
  
  <p>I successfully received location updates when my main activity implemented <code>android.location.LocationListener</code></p>
  
  <pre><code>if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &amp;&amp; ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
      val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
      val criteria = Criteria()
      val provider = locationManager.getBestProvider(criteria, false)
      val location = locationManager.getLastKnownLocation(provider)
  
      locationManager.requestLocationUpdates(provider, 5000, 20f, this)
      setLocation(location)
  } else {
      /* ... */
  }
  </code></pre>
  
  <p>However I attempted to add this code to a new service class (implementing both <code>android.app.Service</code> and <code>android.location.LocationListener</code>), and the <code>onLocationChanged</code> function is only firing when the app is in view.</p>
  
  <p>I believe this problem could be one of the following:</p>
  
  <ol>
  <li><p>I think I read somewhere that I have to manually create a new thread inside the service so that it does not run on the same thread as the main activity. This could be the issue, but I haven't a clue how to do this. I'm new to android development and Kotlin.</p></li>
  <li><p>I think I read somewhere that the location permissions I currently request aren't enough when accessing location in the background, but can't find out what to change.</p></li>
  <li><p>I saw a solution somewhere where they create a timer that runs every few seconds and manually requests the last location from the location manager. I haven't tested this, but if it does work it doesn't satisfy my needs. The <code>requestLocationUpdates</code> allows me to configure both a minimum time and minimum distance. Of course I could manually add in a distance check, but this could affect battery life and performance.</p></li>
  </ol>
  
  <p>How do I fix this problem?</p>
  
  <p>(I can probably change any Java code to Kotlin if anyone is not familiar with Kotlin)</p>
  
  <hr>
  
  <p><strong>Edit 1</strong></p>
  
  <ul>
  <li><p>My service is definitely running, as the location updates are received in the foreground</p></li>
  <li><p>Here is the <code>onStartCommand</code> code for my service:</p></li>
  </ul>
  
  <pre><code>override fun onStartCommand(intent: Intent, flags: Int, startid: Int): Int {
      /* request location updates as above */
      return START_STICKY
  }
  </code></pre>
  
  <ul>
  <li>Here is how I start the service in my main activity:</li>
  </ul>
  
  <pre><code>val serviceIntent = Intent(applicationContext, OnTheFlyLandmarkService::class.java)
  startService(serviceIntent)
  </code></pre>
  
  <hr>
  
  <p><strong>Edit 2</strong></p>
  
  <ol start="4">
  <li><p>I also read that I need to display a notification to let users know that my service is running in the background. I tried the following code but this did not help:</p>
  
  <pre><code>val notification = NotificationCompat.Builder(this, CHANNEL_ID)
      .setSmallIcon(R.drawable.messageicon)
      .setContentTitle("My app")
      .setContentText("Accessing location in background...")
      .setAutoCancel(true)
      .setPriority(NotificationCompat.PRIORITY_DEFAULT)
      .build()
  
  with(NotificationManagerCompat.from(this)) {
      notify(id, notification)
  }
  </code></pre></li>
  </ol>
  
  <hr>
  
  <p><strong>Edit 3</strong></p>
  
  <p>I tried what @Michael said: call <code>startForeground</code> inside the service and pass in the notification. This did not work, and I didn't even see a notification.</p>
  
  <pre><code>override fun onStartCommand(intent: Intent, flags: Int, startid: Int): Int {
      val notification = buildNotification("My app", "Accessing location in background...")
      startForeground(NOTIFICATION_ID_SERVICE, notification)
  
      /* request location updates */
      return START_STICKY
  }
  </code></pre>