等待首选项数据存储从 workmanager 中的首选项数据存储检索数据

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

我的应用程序正在后台检查未读电子邮件,问题是我需要保存和检索上次检查电子邮件时的 lastCheckedDate,以便我只能显示新收到的电子邮件。

为了从数据存储中检索数据,我使用 observeLastCheckedDate() 并且我必须使用处理程序调用它,因为如果我不这样做,我会得到:

java.lang.IllegalStateException: 无法在后台线程上调用观察

函数 observeLastCheckedDate() 被调用,但当它完成时(更新 lastCheckedDate),workManager 任务已经完成,但 var lastchecked 日期未更新。

在 main class 中,我通过创建和调用回调来避免这个问题,但在这里不起作用(它使整个应用程序冻结),所以我们需要等待该函数完成或获得一些新的从数据存储中检索数据的方法。

class WorkerMan(private val mContext: Context, workerParameters: WorkerParameters) :
    CoroutineWorker(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(),
            {
                lastCheckedDate = it

                println("LASTCHECKEDDATE LOADED")
            }
        )
    }


    @SuppressLint("RestrictedApi", "WrongThread")
    override suspend fun doWork(): Result {
        withContext(Dispatchers.IO) {
            settingsManager = SettingsManager(getApplicationContext())


            var messageCounter = 0;


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



            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)
                    }
                }


//            println("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")
//            println("WorkManager Started")
//            println("WorkMananager email: " + email)
//            val current = LocalTime.now()
//            println("WorkMananager time: " + current)
//            println("Messages amount: " + messages.size)
//            println("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")

                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"
}

数据存储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
    }


}

对于简单的工作来说,线程化是一个巨大的混乱。 (永远不要让你的线程休眠以等待一个值)

如果你打算在 worker 中使用协程 class。所以不要那样做

有一个替代方案 CoroutineWorker 可以从中扩展您的 class 而不是 Worker

将为您提供doWork()功能的挂起版本

注意:记得添加 -ktx 版本的工作管理器 dependency