Home > Software engineering >  Can't change variable in handler.post method
Can't change variable in handler.post method

Time:08-24

This is my first post on StackOverflow, so please don't kill me for my poor formatting.

I'm trying to make a Work Tracker App, which logs your time of arrival and time of leave in a MySQL database when you press the button in the app.

I want the app to open the correct (is working / is not working) screen when you launch the app, and I kinda managed to make it work with shared preferences, but I figured it would be more reliable if it would request the status from the database.

The table holding the logs looks like this:

user_id | time_of_arrival | time_of_leave

if the user is still in work, there will be a row where he has time_of_arrival, but the time_of_leave field is NULL.

That's what I want to request here:

private fun checkWorking(
    sharedPreferences: SharedPreferences,
    localContext: Context
) : Boolean {
    val userId = sharedPreferences.getString("userId", "").toString()
    var isWorking = false

    if (userId != "") {
        val handler = Handler(Looper.getMainLooper())
        handler.post {
            val field = arrayOfNulls<String>(1)
            field[0] = "user_id"
            val data = arrayOfNulls<String>(1)
            data[0] = userId
            val putData = PutData(
                Database().host   Database().databaseName   "checkWorking.php",
                "POST",
                field,
                data
            )
            if (putData.startPut()) {
                if (putData.onComplete()) {
                    val result = putData.result
                    if(result == "You are working") {
                        isWorking = true
                    }
                }
            }
        }
    }

    return isWorking
}

here is the php part:

<?php
require "DataBase.php";
$db = new DataBase();
if ($db->dbConnect()) {
    if($db->checkWorking("logs", $_POST['user_id'])) {
        echo "Success";
    } else echo "Failure";
}
?>

and

function checkWorking($table, $userId) {
        $userId = $this->prepareData($userId);

        $this->sql = "SELECT * FROM " . $table . " WHERE user_id = '" . $userId . "' AND time_of_leave IS NULL";
        $result = mysqli_query($this->connect, $this->sql);
        if(mysqli_num_rows($result) != 0) {
            return true;
        }
        return false;
    }

(The PHP part works correctly, I just wanted to give full insight about my problem)

My problem is that it always returns false, because I read somewhere that the return finishes faster than the handler.post changing the isWorking variable to true.

How can I fix this issue, I legitimately can't figure out anything else I could try.

Thanks in advance!

CodePudding user response:

yes, the return statement is being called before the handler is done since it will be working on a different thread while the return is still on the main thread.

So, you can solve that by using an interface to return the callback whenever it has been received, first you create the interface as follows:

public interface CallbackListener<T> {
void onSuccess(T response);
}

then you have to modify you method to take this interface as a parameter

private fun checkWorking(
    sharedPreferences: SharedPreferences, 
    localContext: Context,
    callback: CallbackListener<Boolean>) {
val userId = sharedPreferences.getString("userId", "").toString()
var isWorking = false

if (userId != "") {
    CoroutineScope(IO).launch { //running code on background thread
        val field = arrayOfNulls<String>(1)
        field[0] = "user_id"
        val data = arrayOfNulls<String>(1)
        data[0] = userId
        val putData = PutData(
            Database().host   Database().databaseName   "checkWorking.php",
            "POST",
            field,
            data
        )
        if (putData.startPut()) {
            if (putData.onComplete()) {
                val result = putData.result
                withContext(Main) {//returning to main thread
                    if (result == "You are working") {
                         callback.onSuccess(true)
                    } else
                        callback.onSuccess(false)
                }
            }
        }
    }

I used kotlin Coroutines here instead of handler, but it can be applied to both of them.

then you can call your new method as follows:

checkWorking(
        sharedPreferences,
        context,
        object: CallbackListener<Boolean>{
            override fun onSuccess(response: Boolean?) {
                //insert your logic here
            }
        }
    )
  • Related