How to Start a Foreground Service in Android (With Notification Channels)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hey, guys. Welcome to Coding in Flow In this video we will learn how to start a Foreground Service in Android You can imagine a Service as an Activity without a user interface It can be started from another app component like an Activity or BroadcastReceiver and then it keeps running independently from the life cycle of the component that started it So for example we start a service from an activity We close this activity and then the service keeps running and can do some work in the background like network operations, location updates or playing music while the user is not actively interacting with the app Now before Android Oreo we could just keep these services running in the background invisibly and do our work there without the user ever noticing something from it But of course when 20 different apps do this at the same time this will drain a lot of battery and slow down the phone And for this reason we now have some limits to this since Android Oreo or API level 26 And in a nutshell we can't keep a background service running anymore when our app itself in the background Instead the system will just kill the service after a short time But there are different ways to avoid this One way is to use the JobScheduler to do the background work instead And I have a separate video on that which you can find when you click on the little ⓘ in the top right corner of this video Another option is to make the Service a Foreground Service Foreground Services have these persistent notifications which you can't swipe off the screen They are always there as long as the service is running This way the user is aware of it and can see that your app is using up system resources A good example for such a Foreground Service is a music player for example The player should keep playing music while the app itself is in the background And the user is aware of it so he/she should see this notification These foreground services are also very unlikely to get killed by the system In this video we will learn how to create such a foreground service And how to display this notification. But we won't do any actual work in there Because this is very specific to your use case Instead we will just create the service and keep it running like this Now if you know my Notifications video then you also know that since Android Oreo we have to create NotificationChannels in order to be able to show Notifications And this is also the case for foreground services So we will start by creating a NotificationChannel But I won't explain everything in detail Instead you can watch my separate Notification Channels video which you will also find in the top right corner of this video And the same as in our NotificationChannels video we create our channels in the Application class So we go into our "java" folder, into our package We right-click on our package and create a new "Java Class" We simply call it "App" and it has to extend "android.app.Application" We don't change anything else and click OK We create one constant "public static final String" "CHANNEL_ID = " and we give it an id "exampleServiceChannel" for example Then we override onCreate() because in here we want to create our NotificationChannel We call createNotificationChannel() And we create this method below our onCreate() method here private void createNotificationChannel() And again if you want to know what exactly is happening here you have to watch my NotificationChannels video The same as there we check if we are on Android Oreo if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) If we are on Oreo or higher only then we want to create our NotificationChannel So we create a NotificationChannel object We simply call it "serviceChannel" or whatever you want = new NotificationChannel() I put the arguments into separate lines The id is our "CHANNEL_ID" We give it a name "Example Service Channel" And we give it an importance level "NotificationManager.IMPORTANCE" And for a foreground service the notification importance has to be at least "IMPORTANCE_LOW" Here we just gonna choose DEFAULT And down here we close it with a semicolon Now we need a handle to the NotificationManager We call it "manager = getSystemService (NotificationManager.class)" And then we call "manager.createNotificationChannel()" and pass our "serviceChannel". And now this channel will be created as soon as we start our app. Okay, nice Next let's register our app class in the manifest file So we go into our manifest folder and open AndroidManifest.xml Here in our "application" tag we define "android:name" as our ".App" class And that's already it Now our "App" class will wrap our whole application And its onCreate() method will be called as soon as we start our app Next let's prepare our MainActivity layout So we go into our activity_main.xml file We delete this TextView here and change the ConstraintLayout root layout into a LinearLayout which we give the orientation "vertical", a gravity of "center" so everything will be aligned in the middle of the screen and a padding of 16dp so there is a bit room around the edges of the screen In here we put one "<EditText" "match_parent" width "wrap_content" height We set the hint to "Input" and we give it an id so we can find it later in java code android:id="@+id/edit_text_input" When we start the service we can send data to it And to see how this works we will type something into this EditText field and send the String to our service In a real app this could be a URL for example In our case we will just display the input in our notification And we create two buttons. One to start the service and one to stop it "<Button" we make it a full width button "match_parent" width, "wrap_content" height But it doesn't really matter how you set up this layout We give it a text android:text="Start Service" And an "onClick" attribute so we call a method from here directly "startService" in camel case Now let's just copy this button for our stop button and paste it below We change the text to "Stop Service" and the method to "stopService" as well Of course we get these warnings here because we didn't create these methods yet but we will do this in a moment And now we create our Service class So again we right-click on our package and create a new "Java Class" Let's call it ExampleService and it has to extend "android.app.Service" We don't change anything else and click OK And in here we get a warning because we have to override one method onBind() This method is mandatory, but we actually only need this for bound services Bound services are services where other components can communicate back and forth by binding to it But our service will be a so called "started service" Which means that we don't need this method here and we just keep it like this by returning null The method we care about is onStartCommand() because this will be triggered when we start our service And then we can pass an Intent with some information In our case this will be the input from our EditText Now there are two more life cycle methods that we can override onCreate() and onDestroy() onCreate() will be called the first time we create our service onStartCommand() on the other hand will be called every time we call startService() on this service We can actually do this multiple times and every time pass an Intent to it with new information And every time we do this onStartCommand() will be triggered but onCreate() only one time in the life cycle of this service When our service is stopped onDestroy() will be called And when we then create a new service onCreate() will be called again We will keep onCreate() and onDestroy() empty in this example And instead we will just take care of onStartCommand() But before we implement the functionality in to this method let's prepare our MainActivity because there we want to send some data to this service and then use it in onStartCommand() So we switch over into our MainActivity.java file First we create a variable for our EditText "private EditText" we call it "editTextInput" As usual we assign it in onCreate() editTextInput = findViewById(R.id. and the id we gave it "edit_text_input" And then we create these two methods that we set as "onClick" attributes on our two buttons in our layout They have to be public because we set them in XML "void" for the return type. The first one was "startService" and it has to take a View which we give the name "v" otherwise it will crash because with the "onClick" attribute it tries to send a View to this method So it also has to accept it as an argument And first let's get the input from our EditText "String" we call it "input = editTextInput.getText().toString()" because we want to send this to our service And to start our service we create an Intent the same as we do when we start an Activity "Intent" we call it "serviceIntent = new Intent" then we pass a context for which we pass "this" comma And then we pass the class we want to open which is our "ExampleService.class" To send our "input" String here to this service we take this serviceIntent, call .putExtra() Then we have to give it a name which is basically the key so we can identify it later in our service and we write "inputExtra" as a String In a real app you should create constants for these keys But for clarity I will use a hard-coded String in this example And we pass our "input" as the value itself And when we start an activity we call startActivity() and when we want to start a service we call startService() and pass our "serviceIntent" And to stop our service which we want to do in our "public void stopService()" method which has to take a View "v" as well We create the same Intent as here. So we copy this Paste it here And then we simply call stopService() and pass the same Intent Okay, nice. And now let's go into our service class Catch this input here and create our Notification So we go into our ExampleService.java file And we delete this return statement here because we want to return something else But first we want to retrieve the String that we send to the service Again we call it "String input = " then we take this Intent we passed here "intent.getStringExtra()" and then we have to pass the same name as we defined in putExtra(), which was "inputExtra" But again, in a real app you should use constants for this so you don't accidentally put typos in here And then we create our Notification "Notification" we call it "notification" "= new NotificationCompat.Builder" and here we have to pass a context for which we pass "this" again because Service is a Context as well and the NotificationChannel id And this is what we created this CHANNEL_ID constant for We press [Alt + Enter] to import this because this is in our App class And the NotificationChannel is mandatory for a Notification on Android Oreo and higher. Otherwise the Notification will just not show up In this case the system will create its own Notification which says that our app is running in the background But of course it's better when we send our own notification On lower API levels the CHANNEL_ID will just be ignored So we can use this method here on lower API levels as well without anything crashing. And we don't put a semicolon here because now we set the content on our notification .setContentTitle() here we pass a String for the title like "Example Service". This will be the title of our notification We set the text of the notification with setContentText() And here we want to pass our "input" string just to see that we actually send the String to our service In a real app you would use this data to do the necessary background work on it Next we have to set an icon on this notification This is mandatory as well. So let's quickly create an icon For this we go into our "res" folder here Into "drawable". We right-click on "drawable" and create a new "Vector Asset" I'm just gonna keep this Android icon that we already have But I change the name a bit to "ic_android" Click "Next" and "Finish" Now back into our code here ".setSmallIcon()" and we pass "R.drawable.ic_android" Now we can also do something when we click our notification and here we want to open our activity when we do this For this we go above our notification code here We create an Intent that will start our activity We call it "notificationIntent = new Intent()" We pass "this" as the context and then the class we want to open "MainActivity.class" Now to set this intent on a notification we have to create a PendingIntent We call it "pendingIntent = " then we write "PendingIntent" with capital P ".getActivity()" here we have to pass a context as well "this" and I'm gonna continue on the next line because it will get a bit longer We have to pass a RequestCode which we could later use for example to cancel this PendingIntent Doesn't really matter here and we just pass zero Comma. Then we pass our "notificationIntent" which will start our activity Comma and the flag for which we pass zero as well Because the flag just defines what happens when we update this PendingIntent with a new intent, which we don't do in our example So we can just pass zero. Semicolon And now we can set this pendingIntent on our notification and it will start our MainActivity when we click on this notification We do this with setContentIntent() and we pass our "pendingIntent" And lastly, to create this notification we call ".build()" which returns a Notification that we save in this "notification" variable And when we want to display a notification normally we call .notify() on the NotificationManager This is not necessary here because the service does it itself Instead we call startForeground() We pass an id that has to be bigger than zero Comma and the "notification" itself The id is just the identifier for the notification if we later want to update it And down here we have a warning because we have to return something "return" and then we return one of three values START_ ... STICKY, NOT_STICKY or REDELIVER_INTENT These constants define what happens when the system kills our service NOT_STICKY means our service will just be gone and not started again STICKY means the system will restart our service as soon as possible but the intent we passed to it will be null REDELIVER_INTENT means it will be started it again and it will pass the last intent to it again Now this played a bigger role for background services because a foreground is very unlikely to be killed And here we will just pass START_NOT_STICKY Because we don't care about the service when the system kills it Semicolon Now you could also create this notification in onCreate() This way it will only be created one time even if we call startService() multiple times In our case we recreate the Notification every time we call startService() But this makes sense here because we set this "input" as the message of this notification So we want to update this when we call startService() with a new intent Okay, before we test this let's put a comment before "startForeground()" to see what happens when we don't promote this service to the foreground And we have to register this service in the manifest file So again we go into our AndroidManifest.xml file And here at the bottom, below this </activity> closing tag but inside the </application> closing tag we write "<service" And we already get this suggestion for the android:name=".ExampleService" We choose this and close it with a slash and a closing angle bracket And this is all that is necessary to register the service Now let's run our app and test it without a notification Okay, I click START SERVICE We don't see anything. Now I go into the settings Settings Into the Developer options System -> Developer options If you can't find the Developer options on your device I have a separate video where I explain how to activate these Developer options which you can also find in the top right corner And in here we can display the "Running services" And there is our "Foreground Service Example" app which has still its service running but around the one minute mark this will disappear because the system will kill it I will skip a few seconds And it's gone This is because our service was effectively just a normal background service And with these execution limits we can't keep it running So back into our code and into our ExampleService class We delete this comment here before the "startForeground" and run our app again I'm gonna type something in here Hello World Click START SERVICE And as you can see our notification appears and we can't swipe it off because it's a persistent notification And now when we go into our Running services We can see it here again And no matter how long we wait it will not be killed by the system I'm gonna skip a few minutes in this video As you can see our service is still running and we can also completely close this activity And our service keeps running I'm gonna skip some more minutes And as you can see it's still running We can also open our activity when we click on this notification because we set it as the PendingIntent When we type in some new text "New text" Click START SERVICE again We have our "New text" here in this notification But we still have only this one service running So it didn't start a second one Which means onCreate() was only called the first time We can also stop this service when we click on this STOP SERVICE button Our notification disappears And our service is gone from this list here Now instead of calling stopService() from another component you can also stop the service from the inside So when you have done your work you can call stopSelf() which will stop the service And you have to stop it. Either from the outside or from within Because otherwise it will just keep running But stopSelf() of course only makes sense after you have done all the work in the background And stopping the service one time stops the whole service No matter how often you called startService() and how often you triggered onStartCommand() onDestroy() will be triggered when we stop the service and the next time we start it again onCreate() will be called It's also important to note that a normal service does not start a background thread by default So all the work we do in onStartCommand() runs on the UI thread by default which means that if you do some heavy operations in here without creating a separate worker thread and then start your app the app will not respond If you want to know how to create a separate thread I will put a playlist on that into the info-card box in the top right corner as well However there is also a sub-class of Service which automatically creates a worker thread this class is called IntentService and I will make a separate video on that in the future. Here I will just put a comment //do heavy work on a background thread So you don't forget it when you take a look at the code And one more thing. Let's go back into our MainActivity again This startService() method here worked because we called it while our app was still opened If you want to start your service while the app itself is in the background you should call startForegroundService() instead This method tells the system that you want to promote your service to a foreground service as quickly as possible And after calling this method you have a time window of five seconds to call startForeground() in your service class If you don't do this within these five seconds the system will kill your service immediately If you try to call startService() from the background it will throw an illegal state exception and your app will crash Now there are some situations where your app is in on temporary white-list and you can call startService() from the background without a crash But usually when your app is in the background you should just use startForegroundService() And we get a warning because this method is only available on API level 26 So now we could check which API level we are on and then either call startForegroundService() or startService() But there is a convenience method which does this check internally which we get with ContextCompat.startForegroundService() And here we have to pass a context and our "serviceIntent" And when we click on this method and press [Ctrl + B] we can see that it does exactly what I was talking about It checks if we are on API level 26 or higher in which case it calls startForegroundService() and if not, it just calls startService() So just use this ContextCompat.startForegroundService() convenience method here Okay and now let's see this five second window in action We go into our service class again And again I'm gonna put a comment before startForeground() And now let's run our app again Not gonna type anything in and click START SERVICE And switch over into our service list here We can see our service is running 4... 5... and it's gone We only have this five second window But when we delete this comment here it will work as usual "Hello World" START SERVICE There is our service And it survives the five second window Perfect. Okay, that's how you create a foreground service with a Notification If this video was helpful, please leave a like and don't forget to subscribe for more Android tutorials Take care.
Info
Channel: Coding in Flow
Views: 199,138
Rating: 4.9564576 out of 5
Keywords: 2018, android, android development, android studio, android studio tutorial, android tutorial, beginners, java, android service, android service tutorial, android service keeps stopping, android service example, android service onstartcommand, android foreground service, android foreground service example, android foreground service tutorial, android foreground service notification example, android foreground service notification, android foreground service vs background service
Id: FbpD5RZtbCc
Channel Id: undefined
Length: 23min 22sec (1402 seconds)
Published: Fri May 11 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.