milosev.com
  • Home
    • List all categories
    • Sitemap
  • Downloads
    • WebSphere
    • Hitachi902
    • Hospital
    • Kryptonite
    • OCR
    • APK
  • About me
    • Gallery
      • Italy2022
      • Côte d'Azur 2024
    • Curriculum vitae
      • Resume
      • Lebenslauf
    • Social networks
      • Facebook
      • Twitter
      • LinkedIn
      • Xing
      • GitHub
      • Google Maps
      • Sports tracker
    • Adventures planning
  1. You are here:  
  2. Home
  3. Android

startForegroundService and BroadcastReceiver

Details
Written by: Stanko Milosev
Category: Android
Published: 01 May 2022
Last Updated: 26 October 2025
Hits: 1983
  • kotlin
UPDATE: 2025-10-26 LocalBroadcastManager is deprecated

In this example I want to start foreground service, and change text in TextView in MainActivity.

First in Android Studio go to File -> New -> Service -> Service and add a service:

In AndroidManifest.xml add FOREGROUND_SERVICE permission:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
* 22023-04-04 UPDATE *

And service:

<service
	android:name=".MyService"
	android:enabled="true"
	android:exported="true"/>

22023-04-04 * ENDOFUPDATE *

Then in service, in my example that is app\src\main\java\com\milosev\startforegroundserviceandbroadcastreceiver\MyService.kt, override onCreate, and the code so that onCreate looks like:
override fun onCreate() {
	super.onCreate()
	sendBroadcastMessage("test")

	val channelId =
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			createNotificationChannel("my_service", "My Background Service")
		} else {
			// If earlier version channel ID is not used
			// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
			""
		}

	val notificationBuilder = NotificationCompat.Builder(this, channelId)
	val notification = notificationBuilder.setOngoing(true)
		.setContentTitle("test")
		.setContentText("test")
		.setSmallIcon(R.mipmap.ic_launcher)
		.setPriority(1)
		.setCategory(Notification.CATEGORY_SERVICE)
		.build()
	startForeground(101, notification)
}
Where sendBroadcastMessage looks like:
private fun sendBroadcastMessage(message: String) {
	val intent = Intent("")
	intent.putExtra("message", message)
	LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
and createNotificationChannel:
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String {
	val chan = NotificationChannel(
		channelId,
		channelName, NotificationManager.IMPORTANCE_NONE
	)
	chan.lightColor = Color.RED
	chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
	val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
	service.createNotificationChannel(chan)
	return channelId
}
Now MyService.kt looks like:
package com.milosev.startforegroundserviceandbroadcastreceiver

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.location.Location
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }

    override fun onCreate() {
        super.onCreate()
        sendBroadcastMessage("test")

        val channelId =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel("my_service", "My Background Service")
            } else {
                // If earlier version channel ID is not used
                // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
                ""
            }

        val notificationBuilder = NotificationCompat.Builder(this, channelId)
        val notification = notificationBuilder.setOngoing(true)
            .setContentTitle("test")
            .setContentText("test")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(1)
            .setCategory(Notification.CATEGORY_SERVICE)
            .build()
        startForeground(101, notification)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val chan = NotificationChannel(
            channelId,
            channelName, NotificationManager.IMPORTANCE_NONE
        )
        chan.lightColor = Color.RED
        chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(chan)
        return channelId
    }

    private fun sendBroadcastMessage(message: String) {
        val intent = Intent("")
        intent.putExtra("message", message)
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
    }
}
In MainActivity.kt in onCreate I have added:
override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
	setContentView(R.layout.activity_main)

	val broadCastReceiver = object : BroadcastReceiver() {
		override fun onReceive(contxt: Context?, intent: Intent?) {
			val textView = findViewById<TextView>(R.id.textView)
			textView.text = "test"
		}
	}

	LocalBroadcastManager.getInstance(this)
		.registerReceiver(broadCastReceiver, IntentFilter(""))
}
Button click function should look like:
@RequiresApi(Build.VERSION_CODES.O)
fun startForegroundServiceAndBroadcastReceiverClick(view: View) {
	val intent = Intent(this,MyService::class.java)
	startForegroundService(intent)
}
At the end MainActivity.kt looks like:
package com.milosev.startforegroundserviceandbroadcastreceiver

import android.app.Notification
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.localbroadcastmanager.content.LocalBroadcastManager

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val broadCastReceiver = object : BroadcastReceiver() {
            override fun onReceive(contxt: Context?, intent: Intent?) {
                val textView = findViewById<TextView>(R.id.textView)
                textView.text = "test"
            }
        }

        LocalBroadcastManager.getInstance(this)
            .registerReceiver(broadCastReceiver, IntentFilter(""))
    }

    @RequiresApi(Build.VERSION_CODES.O)
    fun startForegroundServiceAndBroadcastReceiverClick(view: View) {
        val intent = Intent(this,MyService::class.java)
        startForegroundService(intent)
    }
}
Please notice in MyService.kt line of code:
val intent = Intent("")
and in MainActivity.kt
LocalBroadcastManager.getInstance(this)
	.registerReceiver(broadCastReceiver, IntentFilter(""))
Otherwise if I want to filter my messages I could instead write MainActivity.kt:
val broadCastReceiver = object : BroadcastReceiver() {
	override fun onReceive(contxt: Context?, intent: Intent?) {
		when (intent?.action) {
			"testFilter" -> {
				val textView = findViewById<TextView>(R.id.textView)
				textView.text = "test"
			}
		}
	}
}

LocalBroadcastManager.getInstance(this)
	.registerReceiver(broadCastReceiver, IntentFilter("testFilter"))
and in MyService.kt:
private fun sendBroadcastMessage(message: String) {
	val intent = Intent("testFilter")
	intent.putExtra("message", message)
	LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
2023-04-04 Update:

\app\src\main\AndroidManifest.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Startforegroundserviceandbroadcastreceiver"
        tools:targetApi="31">

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"/>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
\app\src\main\res\layout\activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="@+id/btnStart"
        app:layout_constraintEnd_toEndOf="@+id/btnStart"
        app:layout_constraintStart_toStartOf="@+id/btnStart"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startForegroundServiceAndBroadcastReceiverClick"
        android:text="@string/start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.726" />

</androidx.constraintlayout.widget.ConstraintLayout>

Request location updates

Details
Written by: Stanko Milosev
Category: Android
Published: 01 May 2022
Last Updated: 02 May 2022
Hits: 1375
  • kotlin
Here I gave one example on how to get GPS location, problem with that solution is that location will be calculated once, and not any more, if you need like every few seconds to receive location, this solution will give you same location all the time.

Here is another example to receive updated location.
First I have added fusedLocationClient as private member variable:

private lateinit var fusedLocationClient: FusedLocationProviderClient
Where FusedLocationProviderClient is imported from:
implementation 'com.google.android.gms:play-services-location:19.0.1'
In onCreate I have initialized fusedLocationClient:
override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
	setContentView(R.layout.activity_main)
	fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)        
}
I need line like
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
For that I need locationRequest and locationCallback
Both locationRequest and locationCallback I have declared as member variables:
private lateinit var locationCallback: LocationCallback
private lateinit var locationRequest: LocationRequest
locationRequest:
locationRequest = LocationRequest.create().apply {
	interval = 5000
	fastestInterval = 50000
	smallestDisplacement = 170f // 170 m = 0.1 mile
	priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback:
locationCallback = object : LocationCallback() {
	override fun onLocationResult(locationResult: LocationResult) {
		locationResult ?: return

		if (locationResult.locations.isNotEmpty()) {
			// get latest location
			val location =
				locationResult.lastLocation
			if (location != null) {
				println(location.latitude.toString())
				println(location.longitude.toString())
			}
		}
	}
}
In app\src\main\AndroidManifest.xml I have added permissions:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
And check for permission:
if (ActivityCompat.checkSelfPermission(
		this,
		Manifest.permission.ACCESS_FINE_LOCATION
	) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
		this,
		Manifest.permission.ACCESS_COARSE_LOCATION
	) != PackageManager.PERMISSION_GRANTED
) {
	if (ActivityCompat.shouldShowRequestPermissionRationale(
			this as Activity,
			Manifest.permission.ACCESS_FINE_LOCATION
		)
	) {
		return
	} else {
		ActivityCompat.requestPermissions(
			this,
			arrayOf(
				Manifest.permission.ACCESS_FINE_LOCATION,
			)
			, 99
		)
	}
}
At the end my MainActivity.kt looks like:
package com.milosev.requestlocationupdates

import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Looper
import android.view.View
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*

class MainActivity : AppCompatActivity() {
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private lateinit var locationCallback: LocationCallback
    private lateinit var locationRequest: LocationRequest

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    }

    fun requestLocationUpdatesClick(view: View) {

        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this as Activity,
                    Manifest.permission.ACCESS_FINE_LOCATION
                )
            ) {
                return
            } else {
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(
                        Manifest.permission.ACCESS_FINE_LOCATION,
                    )
                    , 99
                )
            }
        }

        locationRequest = LocationRequest.create().apply {
            interval = 5000
            fastestInterval = 50000
            smallestDisplacement = 170f // 170 m = 0.1 mile
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }

        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                locationResult ?: return

                if (locationResult.locations.isNotEmpty()) {
                    // get latest location
                    val location =
                        locationResult.lastLocation
                    if (location != null) {
                        println(location.latitude.toString())
                        println(location.longitude.toString())
                    }
                }
            }
        }

        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback,
            Looper.getMainLooper())
    }
}

Write file on internal storage

Details
Written by: Stanko Milosev
Category: Android
Published: 25 April 2022
Last Updated: 25 April 2022
Hits: 1250
  • kotlin
Method to write file:
private fun writeFileOnInternalStorage(mcoContext: Context, sFileName: String?, sBody: String?) {
	val dir = File(mcoContext.getFilesDir(), "mydir")
	if (!dir.exists()) {
		dir.mkdir()
	}
	try {
		val gpxfile = File(dir, sFileName)
		val writer = FileWriter(gpxfile)
		writer.append(sBody)
		writer.flush()
		writer.close()
	} catch (e: Exception) {
		e.printStackTrace()
	}
}

Small example on how to get gps Location

Details
Written by: Stanko Milosev
Category: Android
Published: 11 April 2022
Last Updated: 13 April 2022
Hits: 1667
  • kotlin
Start empty activity like I already explained here.

In order to display design window, locate activity_main.xml, you will find it under Project -> app -> src -> main -> res -> layout:

Open it and add a button:

In activity_main.xml I have added android:onClick="getGps" so my activity_main.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:onClick="getGps"
        tools:layout_editor_absoluteX="166dp"
        tools:layout_editor_absoluteY="441dp" />

</androidx.constraintlayout.widget.ConstraintLayout>
In build.gradle as I already explained here I have added Google Play services com.google.android.gms.location like this:
implementation 'com.google.android.gms:play-services-location:19.0.1'
Now my build.gradle looks like:
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.milosev.getgpslocation"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    implementation 'com.google.android.gms:play-services-location:19.0.1'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
In Android -> app -> manifests -> AndroidManifest.xml I added permissions like:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

In order to request permission I In Project -> app -> src -> main -> java -> com -> milosev -> getgpslocation -> MainActivity.kt I wrote:

if (ActivityCompat.checkSelfPermission(
		this,
		Manifest.permission.ACCESS_FINE_LOCATION
	) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
		this,
		Manifest.permission.ACCESS_COARSE_LOCATION
	) != PackageManager.PERMISSION_GRANTED
) {
	if (ActivityCompat.shouldShowRequestPermissionRationale(
			this,
			Manifest.permission.ACCESS_FINE_LOCATION
		)
	) {
		return
	} else {
		// No explanation needed, we can request the permission.
		requestLocationPermission()
	}
}
So my whole MainActivity.kt looks like:
package com.milosev.getgpslocation

import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices

class MainActivity : AppCompatActivity() {
    private lateinit var fusedLocationClient: FusedLocationProviderClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    }

    fun getGps(view: View) {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                )
            ) {
                return
            } else {
                // No explanation needed, we can request the permission.
                requestLocationPermission()
            }
        }
        fusedLocationClient.lastLocation
            .addOnSuccessListener { location->
                if (location != null) {
                    // use your location object
                    // get latitude , longitude and other info from this
                }

            }
    }

    private fun requestLocationPermission() {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
            ),
            MY_PERMISSIONS_REQUEST_LOCATION
        )
    }

    companion object {
        private const val MY_PERMISSIONS_REQUEST_LOCATION = 99
        private const val MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 66
    }

}
With piece of code:
fusedLocationClient.lastLocation
.addOnSuccessListener { location->
	if (location != null) {
		// use your location object
		// get latitude , longitude and other info from this
	}
I am actually geting the location.
  1. Create Json With Gson
  2. New and improved my list of best applications
  3. New line
  4. Command line building and installing - gradle and adb

Page 6 of 11

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10