[Android] Remove blocking code in MediaParsingService

Geoffrey Métais git at videolan.org
Thu Jun 28 15:15:46 CEST 2018


vlc-android | branch: master | Geoffrey Métais <geoffrey.metais at gmail.com> | Thu Jun 28 15:15:05 2018 +0200| [8db231a729551c2fd6a766e19a2804ad7f6aa24f] | committer: Geoffrey Métais

Remove blocking code in MediaParsingService

> https://code.videolan.org/videolan/vlc-android/commit/8db231a729551c2fd6a766e19a2804ad7f6aa24f
---

 .../src/org/videolan/vlc/MediaParsingService.kt    | 92 ++++++++++++----------
 1 file changed, 50 insertions(+), 42 deletions(-)

diff --git a/vlc-android/src/org/videolan/vlc/MediaParsingService.kt b/vlc-android/src/org/videolan/vlc/MediaParsingService.kt
index e1442f687..c1d5fd9dc 100644
--- a/vlc-android/src/org/videolan/vlc/MediaParsingService.kt
+++ b/vlc-android/src/org/videolan/vlc/MediaParsingService.kt
@@ -39,6 +39,9 @@ import android.support.v7.preference.PreferenceManager
 import android.text.TextUtils
 import android.util.Log
 import kotlinx.coroutines.experimental.*
+import kotlinx.coroutines.experimental.android.UI
+import kotlinx.coroutines.experimental.channels.Channel
+import kotlinx.coroutines.experimental.channels.actor
 import org.videolan.libvlc.util.AndroidUtil
 import org.videolan.medialibrary.Medialibrary
 import org.videolan.medialibrary.interfaces.DevicesDiscoveryCb
@@ -51,6 +54,10 @@ import java.util.*
 private const val TAG = "VLC/MediaParsingService"
 private const val NOTIFICATION_DELAY = 1000L
 
+private const val SHOW_NOTIFICATION = 0
+private const val HIDE_NOTIFICATION = 1
+private const val UPDATE_NOTIFICATION_TIME = 2
+
 class MediaParsingService : Service(), DevicesDiscoveryCb {
     private lateinit var wakeLock: PowerManager.WakeLock
     private lateinit var localBroadcastManager: LocalBroadcastManager
@@ -65,26 +72,25 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
     private var scanActivated = false
 
     private val callsCtx = newSingleThreadContext("ml-calls")
-    private val notificationCtx = newSingleThreadContext("ml-notif")
 
     private val settings by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
 
-    internal var mScanPaused = false
-    private val mReceiver = object : BroadcastReceiver() {
+    internal var scanPaused = false
+    private val receiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
             when (intent.action) {
                 Constants.ACTION_PAUSE_SCAN -> {
                     if (wakeLock.isHeld) wakeLock.release()
-                    mScanPaused = true
+                    scanPaused = true
                     medialibrary.pauseBackgroundOperations()
                 }
                 Constants.ACTION_RESUME_SCAN -> {
                     if (!wakeLock.isHeld) wakeLock.acquire()
                     medialibrary.resumeBackgroundOperations()
-                    mScanPaused = false
+                    scanPaused = false
                 }
                 Medialibrary.ACTION_IDLE -> if (intent.getBooleanExtra(Medialibrary.STATE_IDLE, true)) {
-                    if (!mScanPaused) {
+                    if (!scanPaused) {
                         exitCommand()
                         return
                     }
@@ -98,6 +104,16 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
     private var wasWorking: Boolean = false
     internal val sb = StringBuilder()
 
+    private val notificationActor by lazy {
+        actor<Int>(UI, capacity = Channel.UNLIMITED, start = CoroutineStart.UNDISPATCHED) {
+            for (update in channel) when (update) {
+                SHOW_NOTIFICATION -> showNotification()
+                HIDE_NOTIFICATION -> hideNotification()
+                UPDATE_NOTIFICATION_TIME -> lastNotificationTime = System.currentTimeMillis()
+            }
+        }
+    }
+
     override fun onCreate() {
         super.onCreate()
         localBroadcastManager = LocalBroadcastManager.getInstance(this)
@@ -106,11 +122,13 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
         val filter = IntentFilter()
         filter.addAction(Constants.ACTION_PAUSE_SCAN)
         filter.addAction(Constants.ACTION_RESUME_SCAN)
-        registerReceiver(mReceiver, filter)
-        localBroadcastManager.registerReceiver(mReceiver, IntentFilter(Medialibrary.ACTION_IDLE))
+        registerReceiver(receiver, filter)
+        localBroadcastManager.registerReceiver(receiver, IntentFilter(Medialibrary.ACTION_IDLE))
         val pm = applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
         wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG)
         wakeLock.acquire()
+
+        if (lastNotificationTime == 5L) stopSelf()
     }
 
     override fun onBind(intent: Intent): IBinder? {
@@ -122,12 +140,11 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
             exitCommand()
             return Service.START_NOT_STICKY
         }
-        synchronized(this at MediaParsingService) {
             // Set 1s delay before displaying scan icon
             // Except for Android 8+ which expects startForeground immediately
-            if (lastNotificationTime <= 0L) System.currentTimeMillis()
-            if (AndroidUtil.isOOrLater) forceForeground()
-        }
+
+        if (AndroidUtil.isOOrLater) forceForeground()
+        else if (lastNotificationTime <= 0L) notificationActor.offer(UPDATE_NOTIFICATION_TIME)
         when (intent.action) {
             Constants.ACTION_INIT -> {
                 val upgrade = intent.getBooleanExtra(Constants.EXTRA_UPGRADE, false)
@@ -149,7 +166,7 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
 
     private fun forceForeground() {
         val ctx = this at MediaParsingService
-        val notification = NotificationHelper.createScanNotification(ctx, getString(R.string.loading_medialibrary), false, mScanPaused)
+        val notification = NotificationHelper.createScanNotification(ctx, getString(R.string.loading_medialibrary), false, scanPaused)
         startForeground(43, notification)
     }
 
@@ -292,15 +309,11 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
         exitCommand()
     }
 
-    private fun showNotification() {
+    private suspend fun showNotification() {
         val currentTime = System.currentTimeMillis()
-        synchronized(this at MediaParsingService) {
-            if (lastNotificationTime == -1L || currentTime - lastNotificationTime < NOTIFICATION_DELAY)
-                return
-            lastNotificationTime = currentTime
-        }
-        notificationJob = launch(notificationCtx) {
-            if (!isActive) return at launch
+        if (lastNotificationTime == -1L || currentTime - lastNotificationTime < NOTIFICATION_DELAY) return
+        lastNotificationTime = currentTime
+        notificationJob = launch {
             sb.setLength(0)
             when {
                 parsing > 0 -> sb.append(getString(R.string.ml_parse_media)).append(' ').append(parsing).append("%")
@@ -311,26 +324,21 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
             val updateAction = wasWorking != medialibrary.isWorking
             if (updateAction) wasWorking = !wasWorking
             if (!isActive) return at launch
-            val notification = NotificationHelper.createScanNotification(this at MediaParsingService, progressText, updateAction, mScanPaused)
-            synchronized(this at MediaParsingService) {
-                if (lastNotificationTime != -1L) {
-                    showProgress(parsing, progressText)
-                    try {
-                        startForeground(43, notification)
-                    } catch (ignored: IllegalArgumentException) {}
-                }
+            val notification = NotificationHelper.createScanNotification(this at MediaParsingService, progressText, updateAction, scanPaused)
+            if (lastNotificationTime != -1L) {
+                showProgress(parsing, progressText)
+                try {
+                    startForeground(43, notification)
+                } catch (ignored: IllegalArgumentException) {}
             }
         }
+        notificationJob?.join()
     }
 
-    private fun hideNotification() {
-        launch(Unconfined) {
-            notificationJob?.cancelAndJoin()
-            synchronized(this at MediaParsingService) {
-                lastNotificationTime = -1L
-                NotificationManagerCompat.from(this at MediaParsingService).cancel(43)
-            }
-        }
+    private suspend fun hideNotification() {
+        notificationJob?.cancelAndJoin()
+        lastNotificationTime = -1L
+        NotificationManagerCompat.from(this at MediaParsingService).cancel(43)
     }
 
     override fun onDiscoveryStarted(entryPoint: String) {
@@ -340,7 +348,7 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
     override fun onDiscoveryProgress(entryPoint: String) {
         if (BuildConfig.DEBUG) Log.v(TAG, "onDiscoveryProgress: $entryPoint")
         currentDiscovery = entryPoint
-        showNotification()
+        notificationActor.offer(SHOW_NOTIFICATION)
     }
 
     override fun onDiscoveryCompleted(entryPoint: String) {
@@ -350,7 +358,7 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
     override fun onParsingStatsUpdated(percent: Int) {
         if (BuildConfig.DEBUG) Log.v(TAG, "onParsingStatsUpdated: $percent")
         parsing = percent
-        if (parsing != 100) showNotification()
+        if (parsing != 100) notificationActor.offer(SHOW_NOTIFICATION)
     }
 
     override fun onReloadStarted(entryPoint: String) {
@@ -370,10 +378,10 @@ class MediaParsingService : Service(), DevicesDiscoveryCb {
     override fun onDestroy() {
         progress.postValue(null)
         started.value = false
-        hideNotification()
+        notificationActor.offer(HIDE_NOTIFICATION)
         medialibrary.removeDeviceDiscoveryCb(this)
-        unregisterReceiver(mReceiver)
-        localBroadcastManager.unregisterReceiver(mReceiver)
+        unregisterReceiver(receiver)
+        localBroadcastManager.unregisterReceiver(receiver)
         if (wakeLock.isHeld) wakeLock.release()
         super.onDestroy()
     }



More information about the Android mailing list