Android : Wait for a background task to finish in main thread - java

I am currently creating an Android app and encounter an issue and I haven't solved yet.
I use the Retrofit library to send requests to a server, according to the Retrofit library it is done in a background thread.
I call this Retrofit request in the main thread and I want to wait for this background thread to finish in order to work on its output.
I found a similar question on the forum but I don't know how to implement :
Android: how to wait AsyncTask to finish in MainThread?
I call the request with Retrofit, when the request is finished, the success method of the Callback object starts, but main thread is still running and it reaches the last line before the background task has finished.
How can I force my main thread to wait for the background task to finish ?
My code :
// ... (main thread)
// CALL TO THE SERVER
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(LoginScreen.ENDPOINT).build();
WebServices getFiles = restAdapter.create(WebServices.class);
Callback<Response> callback = new Callback<Response>()
{
#Override
public void success(Response s, Response response)
{
fileAvailable = new String(((TypedByteArray) s.getBody()).getBytes());
//THIS IS EXECUTED WHEN THE BACKGROUND TASK IS FINISHED
}
#Override
public void failure(RetrofitError retrofitError)
{
Log.e(TAG, retrofitError.toString());
}
};
getFiles.getFiles(id, callback);
// I want here to work with fileAvailable, but the code
// reaches this line before the background task is over.
I tried to create a Handler object as in the previous link and call sendEmptyMessage in the success method but it didn't work.
I would be really grateful if somebody could help me.
Thank you in advance,

You don't want to block your main thread, that's the whole point of this library. You need to reconsider how you do this because if you block the main thread the UI will hang (be unresponsive) and the user will be upset. Think about the last time you used an app and everything locked up for several seconds. That is what happens if you block the main thread.
Instead, you should continue your logic in the response handler, or you can call into a function of the outer class from the inner class (callback is the inner class).
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(LoginScreen.ENDPOINT).build();
WebServices getFiles = restAdapter.create(WebServices.class);
Callback<Response> callback = new Callback<Response>()
{
#Override
public void success(Response s, Response response)
{
fileAvailable = new String(((TypedByteArray) s.getBody()).getBytes());
doSomethingOnSuccess();
}
#Override
public void failure(RetrofitError retrofitError)
{
Log.e(TAG, retrofitError.toString());
}
};
//this runs before success(), so show a spinner or something,
//eg:http://stackoverflow.com/questions/9157504/put-a-progressbar-on-actionbar
}
-- Update (per your comment about having multiple background tasks --
If you've got 2 or more background tasks running in parallel (meaning they don't depend on each others output) but you need all of them to complete before you can do anything with them, it would probably look like this:
public class MyActivity extends Activity {
private Result result1 = null;
private Result result2 = null;
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
Callback<Response> callback1 = new Callback<Response>() {
#Override
public void success(Response s, Response response)
{
result1 = new String(((TypedByteArray) s.getBody()).getBytes());
checkComplete();
}
#Override
public void failure(RetrofitError retrofitError)
{
Log.e(TAG, retrofitError.toString());
}
};
Callback<Response> callback2 = new Callback<Response>() {
#Override
public void success(Response s, Response response)
{
result2 = new String(((TypedByteArray) s.getBody()).getBytes());
checkComplete();
}
#Override
public void failure(RetrofitError retrofitError)
{
Log.e(TAG, retrofitError.toString());
}
};
getFiles.getFiles(id, callback1);
otherthing.doSomething(id, callback2);
}
private void checkComplete() {
if (result1 != null && result2 != null) {
doSomethingWithResults();
}
}
private void doSomethingWithResults() {
//update UI
}
}

There's no "last line" for the main thread, it's ongoing. Main thread is not finished after onCreate() is done executing. Just put the code you want to execute on the data into the success handler right where
//THIS IS EXECUTED WHEN THE BACKGROUND TASK IS FINISHED
is.

Related

Exoplayer throws player is accessed on the wrong thread Exception

when I use exoplayer I get a player is accessed on the wrong thread error. How can I solve this?
Non-fatal Exception: java.lang.IllegalStateException: Player is accessed on the wrong
thread.
Current thread: 'main'
Expected thread: 'ConnectivityThread'
See https://exoplayer.dev/issues/player-accessed-on-wrong-thread
The player is started as a service via my BackgroundAudioService.class.
exoPlayer = new ExoPlayer.Builder(getApplicationContext()
.build();
In the main thread my looper is running, the
which updates the UI via exoplayer.getCurrentPosition().
public final Runnable updatePosition = new Runnable() {
#Override
public void run() {
position = BackgroundAudioService.getCurrentPostion();
}
}
myHandler.postDelayed(updatePosition, myHandlerSleep);
I don't know how to solve this problem (which occurs just sometimes), please help.
Thanks
Alejandro
I solved this by calling the status via a handler in the player's event listener. Starting a runnable from the listener which runs only when player.isPlaying() == true.
player.addListener(new Player.Listener() {
#Override
public void onEvents(Player player, Player.Events events) {
Player.Listener.super.onEvents(player, events);
if (events.contains(Player.EVENT_IS_PLAYING_CHANGED)) {
if (player.isPlaying()) {
positionHandler.postDelayed(getCurrentPositionTask,CURRENT_POSITION_SLEEP);
} else {
positionHandler.removeCallbacks(getCurrentPositionTask);
}
}
}
});
public Runnable getCurrentPositionTask = new Runnable() {
#Override
public void run() {
if (exoPlayer != null) {
currentPostion = exoPlayer.getCurrentPosition();
positionHandler.postDelayed(getCurrentPositionTask,CURRENT_POSITION_SLEEP);
}
}
};
The UI calls the current position the same way in a runnable.
I can't say whether this is the best way. but it's going well.
GGK

How to wait for callback to finish before continuing the loop?

I'm trying to add restMedia to restaurantMediaList in onResponse within a for loop. However, when the loop is finished, restaurantMediaList is null. How can I fix this in such a way that it waits for onResponse to be finished first before proceeding with the next iteration?
public void getImages(List<Restaurant> restaurantList, OnImageReceivedCallback callback){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
WordpressAPICall wordpressAPICall = retrofit.create(WordpressAPICall.class);
for(int i = 0; i < restaurantList.size(); i++){
String featuredMediaURL = restaurantList.get(i).get_links().getFeaturedMedia().get(0).getHref();
featuredMediaURL.substring(featuredMediaURL.indexOf("v2")+1);
Call<RestaurantMedia> restMediaCall = wordpressAPICall.getImage(featuredMediaURL);
restMediaCall.enqueue(new Callback<RestaurantMedia>() {
#Override
public void onResponse(Call<RestaurantMedia> call, Response<RestaurantMedia> response) {
RestaurantMedia restMedia = response.body();
restaurantMediaList.add(restMedia);
//callback.onRestaurantListReceived(restaurantModels, restMedia);
}
#Override
public void onFailure(Call<RestaurantMedia> call, Throwable t) {
Log.d("Fail to get media", t.toString());
}
});
}
callback.onImageReceived(restaurantMediaList);
}
Keep in mind that there are restaurantList.size() different threads (each fires a network request) and the only choice you have is to use some lock.
If there is an API that fetches all of the images together please use it and use my 1st code to wait for result.
I also recommend using timeout because if, for some reason, onResponse AND onFailure will not be called, your calling thread will sleep forever. Tweak the timeout as you see fit.
Waiting for each thread to finish separately is extremely time consuming so I'd recommend to let them go asynchronous and continue when all of them are done.
I'll show you both options.
Wait for each one to wait separately:
CountDownLatch countDownLatch = new CountDownLatch(1);
restMediaCall.enqueue(new Callback<RestaurantMedia>() {
#Override
public void onResponse(Call<RestaurantMedia> call, Response<RestaurantMedia> response) {
// Do your thing
countDownLatch.countDown();
}
#Override
public void onFailure(Call<RestaurantMedia> call, Throwable t) {
// Do your thing
countDownLatch.countDown();
}
});
countDownLatch.await(1L, TimeUnit.SECONDS); // join thread with timeout of second
Waiting for all threads together:
public void getImages(List<Restaurant> restaurantList, OnImageReceivedCallback callback){
// Do your thing
CountDownLatch countDownLatch = new CountDownLatch(restaurantList.size());
for(int i = 0; i < restaurantList.size(); i++){
// Do your thing
restMediaCall.enqueue(new Callback<RestaurantMedia>() {
#Override
public void onResponse(Call<RestaurantMedia> call, Response<RestaurantMedia> response) {
// Do your thing
countDownLatch.countDown();
}
#Override
public void onFailure(Call<RestaurantMedia> call, Throwable t) {
// Do your thing
countDownLatch.countDown();
}
});
}
countDownLatch.await(1L * restaurantList.size(), TimeUnit.SECONDS); // join thread with timeout of second for each item
}

java - make if for async task time limit

here is my piece of code:
Thread one = new Thread() {
public void run() {
try {
new LongOperation(finalJson)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.get(30000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
};
one.start();
i want to say if AsyncTask past 30000 MILLISECONDS and didn't finish the job return a message, how i can code this? thanks
I would prefer doing it using an AsyncTask.
Copy-pasting from the link:
AsyncTask enables proper and easy use of the UI thread. This class
allows you to perform background operations and publish results on the
UI thread without having to manipulate threads and/or handlers.
AsyncTask is designed to be a helper class around Thread and Handler
and does not constitute a generic threading framework. AsyncTasks
should ideally be used for short operations (a few seconds at the
most.) If you need to keep threads running for long periods of time,
it is highly recommended you use the various APIs provided by the
java.util.concurrent package such as Executor, ThreadPoolExecutor and
FutureTask.
Said this, configuring an AsyncTask is pretty simple, just create a class like the following:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method works on the UI thread.
//this is the first to run before "doInBackground"
mTextView.setText("we start!");
}
#Override
protected String doInBackground(String... params) {
try {
//do whatever your async task needs to do. This method works async
//you can also call an UI callback from here with the publishProgress method. This will call the "onProgressUpdate" method, and it has to respect his type.
publishProgress("we go on!");
} catch (InterruptedException e) {
Thread.interrupted();
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
//this method works on the UI thread
//it get the "doInBackground" return value.
mTextView.setText(result);
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(String... values) {
//this method works on UI thread, so it can access UI components and ctx
mTextView.setText(values[0]);
}
}
This is a basic example on how to create an AsyncTask, you can use it like this (form activity/fragment):
AsyncTaskExample asyncTask = new AsyncTaskExample();
asyncTask.get(30000, TimeUnit.MILLISECONDS);
This will set a timeout on your async operation. Look here for the exact Exception/return
For any further question, ask freely. Hope this helps
Edit:
I just noticed you have an AsyncTask inside your thread. Since AsyncTask is already async, I would avoid doing this and simply call the AsyncTask with the method I gave you before. In that way the AsyncTask will run up to the given TimeSpan :)
See the code below: It may help you.
CountDownTimer timer = new CountDownTimer(3000,1000) {
#Override
public void onTick(long l) {
}
#Override
public void onFinish() {
//return a message here;
}
};
timer.start();
And if you have an async task. Then do like below in doInBackground method:
#Override
protected Void doInBackground(Void... voids) {
//simply do your job
CountDownTimer timer = new CountDownTimer(3000,1000) {
#Override
public void onTick(long l) {
}
#Override
public void onFinish() {
//return a message here;
return message;
}
};
timer.start();
return your_reult;
}

How to execute AsyncTask in endless loop?

I have AsyncTask class with methods like this(class: ApiConnector):
#Override
protected String doInBackground(Void... voids)
{
return getToken(); //<-- do many the most important things and return String
}
and
#Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
}
and then in my Activity:
new ApiConnector()
{
#Override
public void onPostExecute(String result)
{
Log.d("here: ", result);
}
}.execute();
It work's fine when I execute this one time, but i have to do this in endless loop to take always fresh token like fresh apples in my market. I tried something like that:
while (true)
{
new ApiConnector()
{
#Override
public void onPostExecute(String result)
{
Log.d("here!", result);
}
}.execute();
Thread.sleep(1000);
}
and many many stupid things, but i can't find working way. All thread bussiness is tricky for me. Give me some kick and I manage this for sure.
You don't want to do this. All AsyncTasks run on one thread. If you infinitely loop inside an AsyncTask, you'll starve all other tasks. If you have each task start a new task, then you'll still risk major starvation issues.
If you want to do this (and I'm not sure you really do, but lets ignore that), the correct way is to use a Thread. A Thread can just have a giant while(true) loop and a sleep statement at the end.
Like hrskrs commented I would prefer using a Handler to execute something repeatedly. The main advantage is that postDelayed makes the run() method execute in the main application thread - so you can access and change UI components.
Here's an example:
public class MyTest implements Runnable {
private final static int INTERVAL = 5000;
private Handler mHandler;
private MyTest() {
mHandler = new Handler();
}
public void start() {
run();
}
public void stop() {
mHandler.removeCallbacks(this);
}
#Override
public void run() {
// put here the logic that you want to be executed
mHandler.postDelayed(this, INTERVAL);
}
}

Android - Run response call in main thread

I want to make downloader, which download data and then call function in UI thread. I have this in main activity
onCreate(){
...
dataRepository.downloadIfNewOrEmpty(new DownloadResponse() {
#Override
public void SuccessResponse(Response response) {
// do something in UI
}
});
}
My function downloadIfNewOrEmpty looks for now only simple with sleep()
public void downloadIfNewOrEmpty(final DownloadResponse response){
//final Handler handler = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000);
response.SuccessResponse(ResponseCode.SUCCESS);
/*handler.post(new Runnable() {
#Override
public void run() {
response.SuccessResponse(ResponseCode.SUCCESS);
}
});*/
}catch (Exception e){
// Log...
}
}
}).start();
}
If I run this code, it normally does the job and update my UI. I found this solution with Handler (android.os.Handler) but if I run it without or with Handler (commented version) it works same.
Although without handler function SuccessResponse is run in UI thread?
Thank you
Although without handler function SuccessResponse is run in UI thread?
Yes, because response is instance of DownloadResponse which is passed from UI Thread as parameter to downloadIfNewOrEmpty.

Categories