Search code examples
androidkotlinokhttpbackground-service

okhttp-sse in background


I'm building a mobile app that is meant to get server sent events in a background service. I can get SSE events when the app is open but when the app closes, I no longer receive SSE events even though they are accepted in a background service. Any solutions?

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
<service
    android:name=".NotificationService"
    android:enabled="true"
    android:exported="true"
    android:process=":MyApp_Notifications"/>

MainActivity.kt

startService(Intent(applicationContext, NotificationService::class.java))

NotificationService.kt

class NotificationService : Service() {
    var notifChannelId = "RD_N_D_C"

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        createNotificationChannel()

        val eventSourceListener = object : EventSourceListener() {
            override fun onEvent(
                eventSource: EventSource,
                id: String?,
                type: String?,
                data: String
            ) {
                super.onEvent(eventSource, id, type, data)
                Log.e(TAG, "\nNOTIF\n")
                val data = JSONTokener(data).nextValue() as JSONObject

                var builder = NotificationCompat.Builder(this@NotificationService, notifChannelId)
                    .setSmallIcon(R.drawable.notification_no_bg)
                    .setContentTitle(data.optString("title"))
                    .setContentText(data.optString("text"))
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true)

                with(NotificationManagerCompat.from(this@NotificationService)) {
                    notify(Random.nextInt(100000, 999999), builder.build())
                }
            }

            override fun onClosed(eventSource: EventSource) {
                Log.e(TAG, "\nError - Closed\n")
                super.onClosed(eventSource)
            }

            override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
                Log.e(TAG, "\nError - Failure\n")
                super.onFailure(eventSource, t, response)
            }
        }

        val client = OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.MINUTES)
            .writeTimeout(10, TimeUnit.MINUTES)
            .build()

        val request = Request.Builder()
            .url("https://random.website.that/sends/sse/events")
            .header("Accept", "application/json; q=0.5")
            .addHeader("Accept", "text/event-stream")
            .build()

        EventSources.createFactory(client)
            .newEventSource(request = request, listener = eventSourceListener)

        client.newCall(request).enqueue(responseCallback = object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e(TAG, "\nError - API Failure\n")
            }
            override fun onResponse(call: Call, response: Response) {}
        })

        return START_STICKY
    }
    // The rest is just onBind(), createNotificationChannel() & onTaskRemoved()

Solution

  • I didn't realize that you can't send notifications from a background service. To fix this all you have to do is change it to a foreground service.

    Change

    startService(Intent(applicationContext, NotificationService::class.java))
    

    To

    val notifIntent = Intent(applicationContext, NotificationService::class.java)
    applicationContext.startForegroundService(notifIntent)
    

    You will also need to add the foreground service permission to your AndroidManifest.xml file.

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