简体   繁体   中英

Wait for Preferences Datastore to retrieve data from preferences datastore in workmanager

My app is checking for unread emails in the background, the problem is that i need to save and retrieve lastCheckedDate when i last checked for emails so i can show only newly received emails.

For retrieving data from datastore i use observeLastCheckedDate() and i must call it with handler because if i dont i get:

java.lang.IllegalStateException: Cannot invoke observe on a background thread

Function observeLastCheckedDate() get called but while it finish(updates lastCheckedDate), workManager task is already finished with not-updated var lastchecked date.

In main class i avoid this problem by creating and invoking callback but here that does not work(it makes whole app freeze), so we somehow need to wait for that function to finish or get some new way of retreiving data from datastore.

 class WorkerMan(private val mContext: Context, workerParameters: WorkerParameters) :
Worker(mContext, workerParameters) {

private lateinit var settingsManager: SettingsManager
var lastCheckedDate: Long = 0

@SuppressLint("RestrictedApi", "CheckResult")
val email = inputData.getString("email")
val password = inputData.getString("password")
val token = inputData.getString("token")

fun saveLastCheckedDate(lastCheckedDate: Long) {
    GlobalScope.launch {
        settingsManager.storeLastCheckedDate(lastCheckedDate)
    }
}

 private fun observeLastCheckedDate() {
    settingsManager.lastCheckedDateFlow.asLiveData().observe(
        ProcessLifecycleOwner.get(),
        {
            this.lastCheckedDate = it
        }
    )
}

@SuppressLint("RestrictedApi", "WrongThread")
override fun doWork(): Result {
    settingsManager = SettingsManager(getApplicationContext())



    var messageCounter = 0;

    val handler = Handler(Looper.getMainLooper())
    handler.post {
        observeLastCheckedDate()
    }

  Thread.sleep(1000*10)


            println("**************************************************************************")
            println("**************************************************************************")
            println("WorkManager: Work called")
            println("WorkManager email: " + email)
            println("WorkManager: Last Checked Moment : " + lastCheckedDate.toString())
            println("WorkManager:      Current Moment : " + Instant.now().toEpochMilli())
            println("**************************************************************************")
            println("**************************************************************************")

            try {

                val session = Session.getDefaultInstance(Properties())
                val store = session.getStore("imaps")
                store.connect(
                    "mail.metropolitan.ac.rs",
                    993,
                    email,
                    password
                )
                val inbox = store.getFolder("INBOX")
                inbox.open(Folder.READ_ONLY)


                val messages = inbox.search(
                    FlagTerm(Flags(Flags.Flag.SEEN), false)
                )

                Arrays.sort(
                    messages
                ) { m1: Message, m2: Message ->
                    try {
                        return@sort m2.sentDate.compareTo(m1.sentDate)
                    } catch (e: MessagingException) {
                        throw RuntimeException(e)
                    }
                }


                for (message in messages) {
                    Thread.sleep(1000)
                    messageCounter++

                    if (message.receivedDate.toInstant().toEpochMilli() >= lastCheckedDate) {

                        Thread.sleep(1000)
                        println("=====================================================")
                        println("NOTIFIKACIJA")

                        var title = ""
                        for (element in message.from) {
                            title += element.toString().substringAfter("<").substringBefore(">")
                            title += " "
                        }
                        println("Title :" + title)
                        println("Subject :" + message.subject)
                        println("Datum i vreme : " + message.receivedDate)

                        title.replace("[", "")
                        title.replace("]", "")

                        send(token, message.subject, title)

                    }

                    if (messageCounter > 10) {
                        break

                    }

                }

                saveLastCheckedDate(Instant.now().toEpochMilli())

                println("=====================================================")
                Log.d("WorkManager", "Job finished")

            } catch (e: Exception) {
                Log.d("WorkManager error", "doWork not executed")
                Log.d("WorkManager error", "error: ")
                Log.d("WorkManager error", e.printStackTrace().toString())
            } catch (e: NetworkOnMainThreadException) {
                Log.d("WorkManager error", "doWork not executed")
                Log.d("WorkManager error", "NetworkOnMainThreadException: ")
                Log.d("WorkManager error", e.toString())
            }

    return Result.Success();
}

 }

fun send(to: String?, body: String?, title: String?): String? {
    try {
        val apiKey =
            "****************************************************************"
        val url = URL("https://fcm.googleapis.com/fcm/send")
        val conn = url.openConnection() as HttpURLConnection
        conn.doOutput = true
        conn.requestMethod = "POST"
        conn.setRequestProperty("Content-Type", "application/json")
        conn.setRequestProperty("Authorization", "key=$apiKey")
        conn.doOutput = true
        val message = JSONObject()
        message.put("to", to)
        message.put("priority", "high")
        val notification = JSONObject()
        notification.put("title", title)
        notification.put("body", body)
        message.put("notification", notification)
        val os = conn.outputStream
        os.write(message.toString().toByteArray())
        os.flush()
        os.close()
        val responseCode = conn.responseCode
        println("\nSending 'POST' request to URL : $url")
        println("Post parameters : $message")
        println("Response Code : $responseCode")
        println("Response Code : " + conn.responseMessage)
        val `in` = BufferedReader(InputStreamReader(conn.inputStream))
        var inputLine: String?
        val response = StringBuffer()
        while (`in`.readLine().also { inputLine = it } != null) {
            response.append(inputLine)
        }
        `in`.close()

        println(response.toString())
        return response.toString()
    } catch (e: Exception) {
        Log.d("WorkManager error", "send not executed")
        Log.d("WorkManager error", "error: ")
        Log.d("WorkManager error", e.printStackTrace().toString())
    } catch (e: NetworkOnMainThreadException) {
        Log.d("WorkManager error", "send() not executed")
        Log.d("WorkManager error", "NetworkOnMainThreadException: ")
        Log.d("WorkManager error", e.toString())
    }
    return "error"
}

DataStore class:

class SettingsManager(context: Context) {

    private val dataStore = context.createDataStore(name = "user_settings_preferencess")


    companion object {

        val ENABLE_NOTIFICATIONS = preferencesKey<Int>("ENABLE_NOTIFICATIONS")
        val ENABLE_MAIL_NOTIFICATIONS = preferencesKey<Int>("ENABLE_MAIL_NOTIFICATIONS")
        val LAST_CHECKED_DATE = preferencesKey<Long>("LAST_CHECKED_DATE")

    }


    //Store user data
    suspend fun storeNotifications(enableNotifications: Int) {
        dataStore.edit {
            it[ENABLE_NOTIFICATIONS] = enableNotifications


        }
    }

    suspend fun storeMailNotifications(enableMailNotifications: Int) {
        dataStore.edit {
            it[ENABLE_MAIL_NOTIFICATIONS] = enableMailNotifications

        }
    }

    suspend fun storeLastCheckedDate(lastCheckedDate: Long) {
        dataStore.edit {
            it[LAST_CHECKED_DATE] = lastCheckedDate


        }
    }

    val lastCheckedDateFlow: Flow<Long> = dataStore.data.map {
        it[LAST_CHECKED_DATE] ?: 0
    }
    
    val enableNotificationsFlow: Flow<Int> = dataStore.data.map {
        it[ENABLE_NOTIFICATIONS] ?: 1
    }
    val enableMailNotificationsFlow: Flow<Int> = dataStore.data.map {
        it[ENABLE_MAIL_NOTIFICATIONS] ?: 1
    }


}

That's a HUGE mess with threading for simple work. (Never make your thread sleep to wait for a value)

if you going to use coroutines in the worker class. SO DON'T DO THAT

there is an alternative CoroutineWorker to extend your class from it instead of Worker

it will provide you with suspending version of doWork() function

NOTE: remember to add the -ktx version of the work manager dependency

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM