Home > Enterprise >  How to return a value from a async thread
How to return a value from a async thread

Time:11-26

I am working on a MediaPlayer to play an audio file

Currently works on the main thread

 public boolean playFile(String path) {
    MediaPlayer mp = new MediaPlayer();
    mp.setAudioAttributes(AudioFocusManager.getPlaybackAttributes());
    try {
        mp.setDataSource(path);
        mp.prepare();
        mp.start();
        mp.setOnErrorListener((mp1, what, extra) -> {
            return true;
        });
        mp.setOnCompletionListener( mp1 -> {
        });
        return true;
    } catch (Exception e) {
        return false;
    }
}

but I am trying to move it to an executor

    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.execute(() -> {

    });

Once I move it to the executor, then the second and third returns triggers an error

Unexpected return value

And playFile triggers the following

Missing return statement

I have tried using AtomicReference

AtomicReference<Boolean> correctlyPlayed = new AtomicReference<>(false);
    //set up MediaPlayer
    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.execute(() -> {
        MediaPlayer mp = new MediaPlayer();
        mp.setAudioAttributes(AudioFocusManager.getPlaybackAttributes());
        try {
            mp.setDataSource(path);
            mp.prepare();
            mp.start();
            mp.setOnErrorListener((mp1, what, extra) -> {
                return true;
            });
            mp.setOnCompletionListener( mp1 -> {
            });
            correctlyPlayed.set(true);
        } catch (Exception e) {
            AudioFocusManager.releaseAudioFocus();
            correctlyPlayed.set(false);
        }
    });

    return correctlyPlayed.get(); 

but it triggers always false, meaning that it does not wait for the thread to update it

How could I do it?

CodePudding user response:

As the comment said, use executor#submit instead of executor#execute, so you can get a Future, and you can call Future#get to get the result:

       Future<Boolean> future = executor.submit(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                // ...
                return true;
            }
        });
        return future.get(); // block until get result

CodePudding user response:

The execute method requires a Runnable and a Runnable does not return anything, that's why you get the unexpected return value error. You can't get your playFile's return value from a separate thread directly like this, but your approach with the AtomicReference should work. There is a built-in atomic type for boolean values, AtomicBoolean, consider using that instead.

Try changing this:

mp.setOnErrorListener((mp1, what, extra) -> {
    return true;
});

To this:

mp.setOnErrorListener((mp1, what, extra) -> {
    correctlyPlayed.set(true);
});

Also, you might want to synchronize your code to wait for the other thread to execute before returning, for that, consider checking out some semaphore synchronization techniques(e.g. CountdownLatch) and I suggest you to read up about the wait-notify mechanism too. The reason behind always getting false as a return value is most probably a synchronization issue, as you pointed out correctly.

  • Related