android thread.join() not working as expected - android

i'm trying to achieve the following: do a single synchronous http request using Volley's Futures in a background thread, and have the current thread wait for the background thread to finish, so it can later handle the http response.
What actually happens is that after the .join(), everything seems to hang and I never enter the breakpoints inside the runnable's run method or get through to any command after the join().
NOTE - the current thread is an activity class, and this specific function is of a Java class whose services are called by the activity. I am aware that .join() on a UI thread causes it to hang...but only until the background thread is finished, right? Well, when I use .wait() instead of .join(), the background thread finishes really fast. It's almost as if calling join doesn't let the background thread do it's job.
private String requestConversionRatesFromApi() throws InterruptedException
{
final JSONObject[] localResponse = {null};
Runnable runnable = new Runnable()
{
#Override
public void run()
{
String url = "myurl";
RequestQueue queue = Volley.newRequestQueue(_context);
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(url, new JSONObject(), future, future);
queue.add(request);
try {
JSONObject jsonObject = future.get();
localResponse[0] = jsonObject; //doesn't get here either unless I'm using .wait() and then it happens really fast
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(runnable);
t.start();
t.join();
return "foo"; //Doesn't get here
}

Related

Running a different thread does not allow the activity to be displayed

I have this piece of an activity:
public class ResultActivity extends AppCompatActivity implements ResultListener {
private String code = "";
private String data = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_result);
code = intent.getStringExtra("code");
data = intent.getStringExtra("data");
MyExternal.DecodeAndSend(this, code, data);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Where MyExternal is a class in other library.
The method DecodeAndSend is something like this:
public static boolean DecodeAndSend(ResultListener caller, String codigo, String data)
{
try {
ExecutorService pool = Executors.newFixedThreadPool(1);
HashMap<String,String> arguments = new HashMap<>();
Future<String> resultado = pool.submit(new ServerConnection(caller, url, arguments));
String status = resultado.get();
if (status.equals("OK"))
caller.OnSuccess();
else
caller.OnError(status);
pool.shutdown();
return true;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return false;
}
Finally, ServerConnection class implements Callable<String> so I show you the call method:
#Override
public String call() throws Exception {
Thread.sleep(2000);
return "OK";
}
The call to Thread.sleep(2000); is actually a call to a web server to send some data.
The problem is that the ResultActivity does not show its layout until the call call returns.
What is missing in this code?
DecodeAndSend is called from the main thread. It calls Future.get() which waits for the job to finish, so it's blocking the main thread. You should call this method from a background thread as well. I think it would be okay to send it to your same thread pool since it is submitted after the first job that it will wait for.
You cannot return anything about the request results from this method, because it is asynchronous.
public static void DecodeAndSend(ResultListener caller, String codigo, String data)
{
ExecutorService pool = Executors.newFixedThreadPool(1);
HashMap<String,String> arguments = new HashMap<>();
Future<String> resultado = pool.submit(new ServerConnection(caller, url, arguments));
pool.submit(new Runnable() {
public void run () {
try {
String status = resultado.get();
if (status.equals("OK"))
caller.OnSuccess();
else
caller.OnError(status);
pool.shutdown();
return;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
caller.OnError(null); // No status, only an exception
});
}
However, your ServerConnection class already takes a caller parameter, so it should probably just handle the callback itself. And depending on what you're doing in the callback, you might want to post the callback calls to the main thread.
By the way, convention in Java is to always start method names with a lower-case letter (camel case).
Feature.get() is a blocking call. The UI Thread is blocked waiting for that call to return, hence can't take care of drawing your layout. Try passing the result listener to ResultListener to the ServerConnection and use the two callbacks to update your UI accordingly
Future.get() is a blocking call - execution stops until the result arrives
The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.
So your Activity's onCreate method calls that stuff, and then blocks until call (which is running on another thread) returns its result. So onCreate doesn't finish, and the layout doesn't complete.
If you want to use that blocking code, but after the view has laid out, I'd use another part of the Activity lifecycle like onStart (set a flag so you only run it once!). Otherwise you'll need to use some other concurrency technique to get your result and use it. It depends on what you're actually doing with the result of your call function

How can I do sth on the end of updating?

There is my code and action after sink.close(); should change text of the textview but it isn't happening. Why?
protected void runNet() throws IOException {
try{
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("http://uprojects.pl/radiowezel/questions.db")
.build();
Response response = client.newCall(request).execute();
QuestionsDB db = new QuestionsDB(context);
File downloadedFile = new File(context.getDatabasePath("questions.db"), "");
BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile));
sink.writeAll(response.body().source());
sink.close();
status.setText("Baza została zaktualizowana");
}catch(UnknownHostException e){
status.setText ("Brak połączenia przy pobieraniu");
}
}
You are trying to update UI from background thread which is not allowed in Android, you should update it from main thread.
You can use runOnUiThread method (part of Activity class)
private void updateUi(final String string) {
runOnUiThread(new Runnable() {
#Override
public void run() {
status.setText(string);
}
});
}
you can use followed method, call it from background thread like this:
updateUi("Baza została zaktualizowana");
Or use AsyncTask which has callback which runs after execution on main thread - AsyncTask Android example
Also you can use Handler - Running code in main thread from another thread

Android Volley RequestFuture always timing out

I want to load some data on application startup that will be used by all activities.
I do this in the application's onCreate method and because I want this data to be downloaded before any activity is shown, I try to make a synchronous request using RequestFuture. However the TimeoutException is always thrown.
public void onCreate() {
super.onCreate();
appRequestQueue = Volley.newRequestQueue(this);
RequestFuture<JSONArray> requestFuture = RequestFuture.newFuture();
JsonArrayRequest request = new JsonArrayRequest(
URL,
requestFuture,
requestFuture);
appRequestQueue.add(request);
try {
JSONArray jsonArray = requestFuture.get(90, TimeUnit.SECONDS);
// Do processing. Never gets here
Log.v("*******************", "Application initialized");
} catch (JSONException je) {
} catch (InterruptedException ie) {
} catch (ExecutionException ee) {
} catch (TimeoutException te) {
// Always times out
Log.v("$$$$$$$$$$$$$$$$$$$$$$$$$", "It has timed out...");
}
What is the best way to get app-wide data keeping in mind that there is no one activity that is started first?
Yeah, according to the default behavior of the volley, the wake up notification (the delivery of the response) of the Request is posted on the Handler of the main UI thread.
So when the UI thread is blocked, the wake up notification will never be executed (also the response will never be delivered), and you'll always get a timeout.
I ran into this recently, the solution is to NOT make requests or handle them from the main thread ( UI ), it always throws a TimeoutException . Consider creating a AsyncTask and performing this request from there.
You can communicate with the MainActivity using the onPostExecute callback.
https://developer.android.com/reference/android/os/AsyncTask.html

Synchronized blocking in multi-threaded app

Below you see some code that works fine - but only once. It is suppsed to block until the runOnUIThread is finished. And it does, when it runs the first time it is called. But when called the second time, it runs through to the end, then the runOnUIThread starts running. It could be, that after the methos was run the first time, the thread that caled it still has the lock, and when it calls the method the second time, it runs through. Is this right? And what can I do to fix that? Or is it a timing problem, the second time the caller gets the lock first?
static Integer syn = 0;
#Override
public String getTanFromUser(long accid, String prompt) {
// make parameters final
final long accid_int = accid;
final String prompt_int = prompt;
Runnable tanDialog = new Runnable() {
public void run() {
synchronized(syn) {
tanInputData = getTANWithExecutionStop(TransferFormActivity.this);
syn.notify() ;
}
}
};
synchronized(syn) {
runOnUiThread(tanDialog);
try {syn.wait();}
catch (InterruptedException e) {}
}
return tanInputData;
}
Background: The thread that calls this method is an asynctask inside a bound service that is doing transactions with a bank in the background. At unregular intervalls the bank send requests for user verification (captche, controll questions, requests for pin, etc.) and the service must display some dialogs vis a weak-referenced callback to the activities in the foreground. Since the service is doing several nested while-loops, it is easier to show the dialogs synchroniously than stopping an restarting the service (savind/restoring the state data would be too complex).
You could try if using a Callable inside a FutureTask instead of a Runnable works better. That combination is as far as I understand meant to provide return values from threads.
public String getTanFromUser(long accid, String prompt) {
// make parameters final
final long accid_int = accid;
final String prompt_int = prompt;
Callable<String> tanDialog = new Callable<String>() {
public String call() throws Exception {
return getTANWithExecutionStop(TransferFormActivity.this);
}
};
FutureTask<String> task = new FutureTask<String>(tanDialog);
runOnUiThread(task);
String result = null;
try {
result = task.get();
}
catch (InterruptedException e) { /* whatever */ }
catch (ExecutionException e) { /* whatever */ }
return result;
}
A Callable is like a Runnable but has a return value.
A FutureTask does the synchronization and waits for the result. Similar to your wait() / notify(). FutureTask also implements Runnable so it can be used for runOnUiThread.

Getting NetworkOnMainThreadException inside a runnable, when targeting 3.0+

I changed my Manifest to target API 16 because of a thing with ActionBarSherlock, and since then my handler that checks the currently playing song is no longer working. It throws a NetworkOnMainThreadException on the line I marked below.
What am I doing wrong? I thought I had multithreading set up right.
Here's my code:
handler = new Handler();
updateSongTask = new Runnable() {
public void run() {
Log.d("asdf", "scraper started");
Scraper scraper = new ShoutCastScraper(); // THIS LINE throws the exception
List<Stream> streams;
try {
streams = scraper.scrape(new URI(url));
for (Stream stream : streams) {
Intent songIntent = new Intent(CUSTOM_INTENT);
String[] songAndArtist = songAndArtist(stream.getCurrentSong());
songIntent.putExtra("song", songAndArtist[0]);
songIntent.putExtra("artist", songAndArtist[1]);
songIntent.putExtra("stationurl", url);
sendBroadcast(songIntent);
Log.d("asdf", "should send broadcast" );
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
handler.postDelayed(this, 5000);
}
};
handler.postDelayed(updateSongTask, 0);
postDelayed() tells Android to run the Runnable on the main application thread after a delay. It does not run the Runnable on a background thread.
CommonsWare was right. I simply put an ASyncTask inside my handler and moved all my song update (worker) code into the doInBackground() method. Works great on all versions, and no network exception!

Categories

Resources