A Beginner's Guide to Android Services, Broadcast Receivers, and View Binding

A Beginner's Guide to Android Services, Broadcast Receivers, and View Binding

Services in Android

Service is an Application Component, that can perform long-running operations in the background.

Doesn't provide a user interface & can be invoked from another application component. Eg - Download File Service starts from the Button Component.

Types of Services :

  1. Foreground Services
    The Process is visible to the user. Hence they display a notification E.g. - Downloading a File (Seekbar shows progress) or playing an audio track.

  2. Background Services

    The Process isn't visible to the user. E.g. - Gallery compressing images to store them.

  3. Bound Services

    Client-Server Relation between component and service, hence doesn't run in the background. A bound service runs only as long as another application component is bound to it.


Types of Service Class :

  1. Service Class

    Traditional Service Class, which uses the main thread of the application. Android OS gives each application a share of the processor, hence slowing down the application when the service has high requirements or is large.

    E.g. - Downloading Slows down when you are simultaneously using that application, as the share of the service is limited.

     <!--Defining the service in AndroidManifest.xml file -->
     <application> .. 
         <service android:name = "ClassicServiceExample"/>
     </application>
    
     //MainActivit.kt
     btn.setOnClickListener { 
         val intent = Intent(this@MainActivity,ClassicServiceExample::class.java)
         startService(intent)
         ...
         stopService(intent)    //runs onDestroy() method of service. 
     }
    
     //Creating a new Kotlin Class :  ClassicServiceExample.kt
     //extending it with our Service Class & Implementing its members
     class ClassServiceExample : Service(){
         override fun onBind(p0: Intent?) : IBinder? {
             return null
             //as we are on not implementing a Bound Service
         }
    
         override fun onCreate(){
             //runs when service created
             super.onCreate()
         }
    
         override fun onStartCommand(intent : Intent?, flags : Int, startId: Int) : Int {
             //write service here. 
             //runs when service started
             //which uses the main thread.
    
             //stopSelf() method when called inside this function, will destory the service & go to onDestroy() callback
             return super.onStartCommand(intent, flags, startId)
         }
    
         override fun onDestroy(){
             //runs when service destroyed
             super.onDestroy()
         }
     }
    
  2. Job Intent Service Class

    Sub-class of our standard service class doesn't use the same application thread but creates a new one. Hence used in resource-intensive tasks in the background.

    However, WorkManager is much preferred than the Job Intent Serive class & is also depreciated.

     <!--Defining the service in AndroidManifest.xml file -->
     <users-permission android:name = "android.permission.WAKE_LOCK"/>
    
     <application> .. 
         <service android:name = "JobIntentServiceExample"    
                  android: permission = "android.permission.BIND_JOB_SERVICE"/>
     </application>
    
     //MainActivit.kt
     btn.setOnClickListener { 
         val intent = Intent(this@MainActivity,JobIntentServiceExample::class.java)
         JobIntentServiceExample.myBackgroundService(this@MainActivity,intent) 
     }
    
     //Creating a new Kotlin Class :  JobIntentServiceExample.kt
     //extending it with our Service Class & Implementing its members
     class ClassServiceExample : JobIntentService(){
         override fun onHandleWork(intent: Intent) {
             //service started
             //executes the enqueued work
         }
    
         companion object{
             fun myBackgroundService(context: Context, intent: Intent){
                 enqueueWork(context,JobIntentServiceExample::class.java,1,intent)
                 //enqueue's new work to be handled by the service
                 //takes context, class, int(unique ID), Intent as parameters
             }
         }
    
         override fun onDestroy(){
             //runs when service destroyed
             super.onDestroy()
         }
     }
    

Broadcast Receivers in Android

A messaging system that allows communication between the Android OS and Android applications, as well as between different applications. This system is used to send messages regarding system events such as booting, charging, battery indication, and more.

Apps can register to receive specific broadcasts. Whenever a system event occurs, a broadcast is sent from the OS or an application, and the system automatically routes broadcasts to apps that have subscribed to receive that particular type of broadcast.

The broadcast message itself is wrapped in an Intent object whose action string identifies the event that occurred (for example android.intent.action.AIRPLANE_MODE). The intent may also include additional information bundled into its extra field. For example, the airplane mode intent includes a boolean extra that indicates whether or not Airplane Mode is on.

Some important wide-generated events:

  1. android.intent.action.BATTERY_LOW: Indicates low battery condition on the device.

  2. android.intent.action.BOOT_COMPLETED: This is broadcast once after the system has finished booting

  3. android.intent.action.CALL: To perform a call to someone specified by the data

  4. android.intent.action.DATE_CHANGED: The date has changed

  5. android.intent.action.REBOOT: Have the device reboot

  6. android.net.conn.CONNECTIVITY_CHANGE: The mobile network or wifi connection is changed(or reset)

Implementing a Broadcast Receiver :

//MainActivity.kt
var br = BroadcastExample()
//registering receiver 

.. onCreate(){
    ...
    val filter = IntentFilter()
//intent filter used to determine which application wants to receive
//which intents
    filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
    this.registerReceiver(br,filter)
//registering the receiver 
}
override fun onStart(){
    super.onStart()
    val filter = IntentFilter()
    filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
    this.registerReceiver(br,filter)
}
override fun onStop(){
    super.onStop()
    this.unregisterReceiver(br)
//unregistered as we only want broadcast messages when our application 
//is running
}
//BroadcastExample.kt
class BroadcastExample : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent : Intent?){
//executed when airplane mode changed
//intent contains info about broadcast
        val isAirplane = intent!!.getBooleanExtra("state",false)
//we take the state of airplane mode from the intent
        if(isAirplane){
        //airplane mode is on
        }
        else{
        //airplane mode is off
        }
    }  
}

View Binding in Android

View binding is a feature that makes it easier to interact with views.

  • generates a binding class for each XML layout present in the module

  • instance of a binding class contains a direct reference to all views that have an id in the layout.

  • replaces the findViewById method.

Enabling View Binding :

//app level gradle file android
android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

If you want a layout file to be ignored while generating binding classes, add the tools:viewBindingIgnore="true" attribute to the root view of that layout file

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

Implementing View Binding :

private lateinit var binding: ResultProfileBinding
//set up instance of the Binding class
//ResultProfile is the module name
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
//inflate() method creates an instance of the binding class for the activity to use.
    val view = binding.root
//getting a reference to the root view
    setContentView(view)
//Pass the root view to setContentView() to present it on screen

    binding.name.text = "hey"
    binding.button.setOnClickListener { .. }
//accessing views by BindingObject.viewID
}