javaandroidandroid-studiopostexecutor

Java POST Issue, how to check return from server?


How to check if is true or false ? Using executor but success seems not to change its value.. there is any trick to make it in a 'easy' way ? Im able to reach inside the try catch, but seems that its not working properly, im not changing its value truly.. maybe if i make success a atribute of the class ? Chatgpt is in crazy mode and seems not to solve the problem for me..

    public Boolean  POST(String payload, String action) {
            
            AtomicReference<Boolean> success = new AtomicReference<Boolean>(false);

        executor.execute(() -> {
            try {


                Gson gJSON = new Gson();
                // Convert payload to a JSON object
                JSONObject jsonObject = new JSONObject(payload);

                // Add the action property to the JSON object
                jsonObject.put("action", action);

                // Convert the modified JSON object back to a string
                String payloadWithAction = gJSON.toJson(jsonObject);
                //System.out.println("Payload to SV"+payloadWithAction);
                // Create URL object
                URL url = new URL("https://moveroute.org/php/run-register-api.php");

                // Open connection
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Content-Type", "application/json");
                connection.setDoOutput(true);
                Response responseObject = new Response();


                // Write JSON data to connection
                try (OutputStream os = connection.getOutputStream()) {
                    os.write(payloadWithAction.getBytes());
                }

                // Check response code
                int responseCode = connection.getResponseCode();
                StringBuilder response = new StringBuilder();
                Response responseJSON = null;
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    // Handle success
                    BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    String inputLine;
                    while ((inputLine = in.readLine()) != null) {
                        response.append(inputLine);
                    }
                    in.close();
                    String jsonResponse = response.toString();
                    //System.out.println("Response from server: " + jsonResponse);

                    responseJSON = gJSON.fromJson(jsonResponse, Response.class);
                    success.set(responseJSON.getStatus().booleanValue());
                    System.out.println(responseJSON.getMessage());
                } else {
                    //ERROR FAIL

                    responseObject.setStatus(false);
                    responseObject.setMessage("Impossible to make the requested action.");

                }

                // Close connection
                connection.disconnect();

            } catch (Exception e) {
                e.printStackTrace();
            }

        });
        return success.get();
    }

Just making the login of an app


Solution

  • The reason that you get false, is because your login code runs on a different thread (executor one) from your "return" thread (I assume it's the main thread), so you have no guarantee that the execution of your "return" code will run after the login part, the context switch between threads is OS decision, which makes your result inconsistent, which means that if you run it many times, you might get true.

    There are many options to fix this, based on your current implementation, the best would be using futures:

    A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled. If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future<?> and return null as a result of the underlying task.

    You can read the documentation in further at: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html#get--

    Example for the implementation:

    class GateWay {
        private Future<Boolean> post(String payload, String action) {
            return executor.submit(() -> {
                // your login code
                // .. 
                return responseJSON.getStatus().booleanValue();
            });
        }
    }
    

    Then you can just call the method as so:

    GateWay gateWay = new GateWay();
    // The "get" method is blocking the main thread and waits for the future thread to wake the main thread up
    if(gateWay.post("Hello", "World").get()) { 
        System.out.println("True is returned");
    }
    else {
        System.out.println("False is returned");
    }
    

    Tip: If not for learning purposes, I don't see any reason not to use any other 3rd party libraries like: Retrofit, OkHttpClient, Apache HttpClient, Spring Web Client, and there are many more, some even support async requests, which you can get advantage of, I personally wouldn't post a request without an http client, I think it's redundant for you to write to output stream yourself, focus on your business logic and let other libraries do their magic.