Source丨Android Bus Android Developer Portal
Link:
Problem
About Foreground Services
Foreground Service
Description:Foreground services perform operations that are noticeable to the user. For example, audio applications will use foreground services to play audio tracks. Foreground services must display a notification. Even if the user does not interact with the application, the foreground service continues to run.
My Code
foreground service
is really a simple process, so I will go through and explain all the steps required to build a never-stopping foreground service.Add Some Dependencies
build.gradle
file:dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:"+kotlin_version
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.jaredrummler:android-device-names:1.1.8'
implementation 'com.github.kittinunf.fuel:fuel:2.1.0'
implementation 'com.github.kittinunf.fuel:fuel-android:2.1.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
Our Service
Foreground Services
need to display notifications so that users know the application is still running.It makes sense if you think about it.log
, setServiceState
) and some custom enums (ServiceState.STARTED
), but don’t worry too much.If you want to know where they come from, check the example repository.class EndlessService : Service() {
private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false
override fun onBind(intent: Intent): IBinder? {
log("Some component want to bind with the service")
// We don't provide binding, so return null
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
log("onStartCommand executed with startId: $startId")
if (intent != null) {
val action = intent.action
log("using an intent with action $action")
when (action) {
Actions.START.name -> startService()
Actions.STOP.name -> stopService()
else -> log("This should never happen. No action in the received intent")
}
} else {
log(
"with a null intent. It has been probably restarted by the system."
)
}
// by returning this we make sure the service is restarted if the system kills the service
return START_STICKY
}
override fun onCreate() {
super.onCreate()
log("The service has been created".toUpperCase())
var notification = createNotification()
startForeground(1, notification)
}
override fun onDestroy() {
super.onDestroy()
log("The service has been destroyed".toUpperCase())
Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show()
}
private fun startService() {
if (isServiceStarted) return
log("Starting the foreground service task")
Toast.makeText(this, "Service starting its task", Toast.LENGTH_SHORT).show()
isServiceStarted = true
setServiceState(this, ServiceState.STARTED)
// we need this lock so our service gets not affected by Doze Mode
wakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "EndlessService::lock").apply {
acquire()
}
}
// we're starting a loop in a coroutine
GlobalScope.launch(Dispatchers.IO) {
while (isServiceStarted) {
launch(Dispatchers.IO) {
pingFakeServer()
}
delay(1 * 60 * 1000)
}
log("End of the loop for the service")
}
}
private fun stopService() {
log("Stopping the foreground service")
Toast.makeText(this, "Service stopping", Toast.LENGTH_SHORT).show()
try {
wakeLock?.let {
if (it.isHeld) {
it.release()
}
}
stopForeground(true)
stopSelf()
} catch (e: Exception) {
log("Service stopped without being started: ${e.message}")
}
isServiceStarted = false
setServiceState(this, ServiceState.STOPPED)
}
private fun pingFakeServer() {
val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.mmmZ")
val gmtTime = df.format(Date())
val deviceId = Settings.Secure.getString(applicationContext.contentResolver, Settings.Secure.ANDROID_ID)
val json =
"""
{
"deviceId": "$deviceId",
"createdAt": "$gmtTime"
}
"""
try {
Fuel.post("https://jsonplaceholder.typicode.com/posts")
.jsonBody(json)
.response { _, _, result ->
val (bytes, error) = result
if (bytes != null) {
log("[response bytes] ${String(bytes)}")
} else {
log("[response error] ${error?.message}")
}
}
} catch (e: Exception) {
log("Error making the request: ${e.message}")
}
}
private fun createNotification(): Notification {
val notificationChannelId = "ENDLESS SERVICE CHANNEL"
// depending on the Android API that we're dealing with we will have
// to use a specific method to create the notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
val channel = NotificationChannel(
notificationChannelId,
"Endless Service notifications channel",
NotificationManager.IMPORTANCE_HIGH
).let {
it.description = "Endless Service channel"
it.enableLights(true)
it.lightColor = Color.RED
it.enableVibration(true)
it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
it
}
notificationManager.createNotificationChannel(channel)
}
val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, 0)
}
val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
this,
notificationChannelId
) else Notification.Builder(this)
return builder
.setContentTitle("Endless Service")
.setContentText("This is your favorite endless service working")
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("Ticker text")
.setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
.build()
}
}
Time to Handle the Android Manifest
FOREGROUND_SERVICE
, INTERNET
and WAKE_LOCK
.Make sure you don’t forget to include them, as it won’t work.<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.robertohuertas.endless">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".EndlessService"
android:enabled="true"
android:exported="false">
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Starting the Service
MainActivity
, which has only one screen with two buttons to start and stop the service.This is all you need to start our never stopping service.class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "Endless Service"
findViewById<Button>(R.id.btnStartService).let {
it.setOnClickListener {
log("START THE FOREGROUND SERVICE ON DEMAND")
actionOnService(Actions.START)
}
}
findViewById<Button>(R.id.btnStopService).let {
it.setOnClickListener {
log("STOP THE FOREGROUND SERVICE ON DEMAND")
actionOnService(Actions.STOP)
}
}
}
private fun actionOnService(action: Actions) {
if (getServiceState(this) == ServiceState.STOPPED && action == Actions.STOP) return
Intent(this, EndlessService::class.java).also {
it.action = action.name
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
log("Starting the service in >=26 Mode")
startForegroundService(it)
return
}
log("Starting the service in < 26 Mode")
startService(it)
}
}
}
Effect: Start Service on Android Boot
StartReceiver
.class StartReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED && getServiceState(context) == ServiceState.STARTED) {
Intent(context, EndlessService::class.java).also {
it.action = Actions.START.name
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
log("Starting the service in >=26 Mode from a BroadcastReceiver")
context.startForegroundService(it)
return
}
log("Starting the service in < 26 Mode from a BroadcastReceiver")
context.startService(it)
}
}
}
}
Android Manifest
again to add a new permission (RECEIVE_BOOT_COMPLETED
) and our new BroadcastReceiver.<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.robertohuertas.endless">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".EndlessService"
android:enabled="true"
android:exported="false">
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver android:enabled="true" android:name=".StartReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>
adb root
# If you get an error then you're not running the proper emulator.
# Be sure to stop the service
# and force a system restart:
adb shell stop
adb shell start
# wait for the service to be restarted!
Recommended↓↓↓
Long
Press
Close
Note
👉【16 Technical Public Accounts】 are all here!
Covers: Programmer Celebrities, Source Code Reading, Programmer Reading, Data Structures and Algorithms, Hacker Techniques and Network Security, Big Data Technology, Programming Frontend, Java, Python, Web Programming Development, Android, iOS Development, Linux, Database Development, Humorous Programmers, etc.
Leave a Comment
Your email address will not be published. Required fields are marked *