androidfunctionkotlinvariables

How do I make a variable accessable from the TimerTask() run function in Kotlin (Android)?


I am working on a project that consists of functions that retrieve and write to a html_data variable and a timer that calls them every minute.

I save the previous html_data to a prev_html_data variable, and then when they are different, I send a notification.

The problem is that the variable is always an empty string (the way I initialized it), meaning it never gets overriden. html_data works just fine, and is initialized the exact same way.

I even seperated everything into a function, so I can call it from the main class, but it still doesn't work.

Variable is always an empty string, no matter what I do... Every time I print it...

And also I forgot to mention, the html_data that I print in the timerRun() was never parsed by html_parse() for some reason, even though I call that function in fetchdata().

class MainActivity : AppCompatActivity() {
    var unique_id = 1000
    var html_data = ""
    var prev_html_data = ""
    lateinit var notificationManager: NotificationManager
    lateinit var notificationChannel: NotificationChannel
    lateinit var builder: Notification.Builder
    private val channelId = "hallomotto"
    private val description = "***** alert"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        fetchdata()
        this.prev_html_data = html_data
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationChannel = NotificationChannel(channelId, description, NotificationManager.IMPORTANCE_HIGH)
            notificationChannel.enableLights(true)
            notificationChannel.lightColor = Color.GREEN
            notificationChannel.enableVibration(true)
            notificationManager.createNotificationChannel(notificationChannel)

            builder = Notification.Builder(this, channelId)
                .setContentTitle("Floria alert!!")
                .setContentText("A new message has been posted (Floria is here!!!)")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setLargeIcon(BitmapFactory.decodeResource(this.resources, R.drawable.ic_launcher_background))
        } else {

            builder = Notification.Builder(this)
                .setContentTitle("Floria alert!!")
                .setContentText("A new message has been posted (Floria is here!!!)")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setLargeIcon(BitmapFactory.decodeResource(this.resources, R.drawable.ic_launcher_background))
        }

        val timer = Timer(true)
        timer.schedule(object : TimerTask() {
            override fun run() {
                timerRun()
            }
        }, 60000, 60000)
    }

    private fun fetchdata() {
        val volleyQueue = Volley.newRequestQueue(this)
        val url = "*****"

        val jsonRequest = JSONObject()
        jsonRequest.put("key", "*****")

        val jsonObjectRequest = JsonObjectRequest(
            Request.Method.POST,
            url,
            jsonRequest,
            { response ->
                val html_message = response.get("html_data")
                html_data = html_message.toString()
                display_html()
            },
            { error ->
                Toast.makeText(this, "Error has occurred when making the request", Toast.LENGTH_LONG).show()
                Log.e("MainActivity", "load_error: ${error.localizedMessage}")
            }
        )
        volleyQueue.add(jsonObjectRequest)
        Log.v("JSON request", jsonRequest.toString())
    }

    private fun html_parse() {
        html_data = """
            <html>
                <head>
                </head>
                <body>
        """.trimIndent() + html_data
        html_data += """
                </body>
            </html>
        """.trimIndent()
    }

    private fun display_html() {
        html_parse()
        Log.v("Output", html_data)
        val encoded_data = Base64.encodeToString(html_data.toByteArray(), Base64.NO_PADDING)
        val webview = findViewById<WebView>(R.id.webview)
        webview.loadData(encoded_data, "text/html", "base64")
    }

    private fun timerRun(){
        fetchdata()
        if(html_data != this.prev_html_data){
            notificationManager.notify(unique_id, builder.build())
            unique_id += 1
            Log.e("Match 1", html_data);
            Log.e("Match 2", this.prev_html_data)
        }
        this.prev_html_data = html_data
    }
}

Solution

  • Volley requests are asynchronous. volleyQueue.add() returns immediately, and the request will be processed some time later. You get the results in the response lambda.

    As a result, when you call fetchdata(), the data is not fetched yet by the time fetchdata() returns. However, your current code expects that the data is fetched, because it attempts to read html_data (this.prev_html_data = html_data). That will still be the empty string.

    Instead, you could have fetchdata() fill in prev_html_data as needed, such as on the first time you are loading the data, using the same response lambda as you are using to set up html_data.