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

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

Related

AsyncTask update UI multiple times

I'm trying to update the UI multiple times in an AsyncTask.
First of all the UI should update, if a request was accepted and later it should run the publishProgress, but If I return a value in the requestAccepted method the other acceptedFiles method will never be executed, but I want it to execute and update the UI before from this task
#Override
protected Void doInBackground(FileInformationHandler... params) {
try {
handler.createSecureSocket("192.168.3.29", 7431);
ProtocolHandler phandler = new ProtocolHandler(handler.getInputStream(), handler.getOutputStream());
phandler.sendInitialisation();
ConfirmationHandler cHandler = new ConfirmationHandler(handler.getInputStream(), handler.getOutputStream());
cHandler.addListener(new ConfirmationReceivedListener() {
#Override
public void requestAccepted(boolean b) {
// Update UI without stopping the asynctask
}
#Override
public void acceptedFiles(int[] ints) {
fileSender.addListener(new ProcessListener() {
#Override
public void processChanged(int i, long l) {
publishProgress(i);
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
First of all you don't need to use an Async task if you are anyways doing the long running operation in a separate thread and have callbacks for different states.
So in your case why can't you simply so all of this in the main thread and in the callbacks just update the UI.
But if you still want to use the Async task then since the doInBackground executes in a separate thread, you can do whatever long running operation is in the method serially and keep using the publishProgress method to update the UI whenever you want. since you are using the callback interface in your case the method will return null and the control with go into the 'onPostExecute'.
IMO using a callback interface in the doInBackground method is not the right approach and defeats the purpose of asyncTask async task.

How to handle variables shared between UI thread and another thread in android?

I am trying to develop an audio processing related app in android. I have one thread(not the UI thread) in which I am doing an operation. I need to update the result of the operation while it is still going on. For the same I am using a Handler. I am just using a Toast to display the result inside the handler. As of now my thread is continuing to run for the first time alone and after displaying the first result the thread doesn't run anymore because of which results are not updated. I just came to know that while modifying variables shared by this Thread and the UI, I need to synchronize both the threads. Am I correct? If so how can I achieve it?
Thanks!!
EDIT
I am posting a part of the method which is running in my thread and my handler.
while(fulldatabuffcnt+200<=fulldatabuffer.size())
{
double[] windowdata=new double[200];
classlabel=0;
//classlabel_new=0;
int windowcnt=0;
for (int h=fulldatabuffcnt;h<fulldatabuffcnt+200;h++)
{
windowdata[windowcnt]=fulldatabuffer.get(h);
windowcnt++;
}
MFCCcoeffs=mfcc_inst.getParameters(windowdata);
classlabel=likeli_ref.llhmain(MFCCcoeffs);
try {
out.writeInt(fulldatabuffer.size());
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
classlabel_array[ecount]=classlabel;
ecount++;
if (ecount==25)
{
synchronized(SharedData.globalInstance) {
SharedData.globalInstance.classlabel_new =occurence(classlabel_array);//<--shared variable classlabel_new getting modified
}
try {
out_max.writeInt(SharedData.globalInstance.classlabel_new);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ecount=0;
uiCallback.sendEmptyMessage(0);
}
fulldatabuffcnt=fulldatabuffcnt+80;
}
if(fulldatabuffcnt+200>fulldatabuffer.size()){
AppLog.logString("Setting calclating thread to null");
calculatingThread = null;
}
try {
out.close();
out_max.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
private Handler uiCallback = new Handler () {
public void handleMessage (Message msg) {
int label_handler;
synchronized(SharedData.globalInstance) {
label_handler=SharedData.globalInstance.classlabel_new;
}
Toast.makeText(MFCC2Activity.this, "Classified label" +label_handler, Toast.LENGTH_SHORT).show();//<--trying to access classlabel_new
}
};
Yes, you should synchronize to make sure that your UI thread doesn't access variables that are only partially set up by your own thread.
I suggest that you have a singleton object what contains all the variables/data etc that you need to pass between the two threads. For example, suppose you need to share a string and a double between your own thread and the UI thread. Create a class SharedData with a singleton, e.g.
class SharedData {
public String aString;
public double aDouble;
public static SharedData globalInstance = new SharedData();
}
Then in your own thread where you want to set the data
synchronized(SharedData.globalInstance) {
SharedData.globalInstance.aString = "some string";
SharedData.aDouble = 42.0;
}
and in your UI thread
String aString;
double aDouble;
synchronized(SharedData.globalInstance) {
aString = SharedData.globalInstance.aString;
aDouuble = SharedData.aDouble;
}
// do something with aString and aDouble
If you do it like that, then there won't be any problems relating to partially set data being read by the UI thread.

Calling Drive API for Android on Separate thread returns no data

I am using the Drive API v2 for android, and when I execute the following method my app seems to pause or wait, and no data is returned.
public About getAbout() throws InterruptedException, ExecutionException {
FutureTask<About> future = new FutureTask<About>(new Callable<About>() {
public About call() throws IOException {
About about = null;
try {
about = _driveService.about().get().execute();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return about;
}
});
About about = future.get();
return about;
}
Does anyone have any idea what I'm doing wrong?
You are creating a FutureTask, but you are never executing it (not on the current thread and not on any other). Then you call future.get() which will block until the operation is completed. Since you never actually perform the operation, it will wait forever.
To execute an operation on a background thread you could for example use http://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.

Show result of long task after a ProgressDialog

I have an application which runs a long task and returns a value. While the task is running, a ProgressDialog shows the progress. After the task is done I want to show the result in a TextView. I run the task in a FutureTask.
My problem is that if I try to get the result, the .get() method of FutureTask blocks the UI Thread and I don't see the ProgressDialog (the TextView displays the result propertly).
My code for the task (pool is ExecutorService):
final FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
#Override
public String call() {
return myLongTask();
}
});
pool.execute(future);
And afterwards I call updateProgressBar() in a Runnable which updates the ProgressDialog with a Handler:
Runnable pb = new Runnable() {
public void run() {
myUpdateProgressBar();
}
};
pool.execute(pb);
Now I'm getting the result, which blocks the UI Thread preventing the ProgressDialog to show up:
String result = future.get()
If I try to put the result inside the updateProgressBar() method (by passing the future as a parameter) after the ProgressDialog dismisses, an Exception is thrown:
Only the original thread that created a view hierarchy can touch its views.
Any ideas how to solve this problem? (I've heard about AsyncTasks but I can't figure out how to use them propertly.)
You are correct that you need either an AsyncTask or a Thread/Handler combination in order to not block the UI.
Neither approach is that tricky, and there are some good guides around that lay out how to do them. Here are some links that I'd recommend.
AsyncTask
Painless threading
Threading
Designing for responsiveness
Thread documentation
Handler documentation
Yes, I had similiar problem when implementing ExecutorService, the following code block the UI thread and need to be run on a separated thread:
String result = future.get()
Just create a class extending AsyncTask to handle the future.get() method like the following code example:
private class FutureTask extends AsyncTask<Future<String>, Void, String>{
#Override
protected PhotoToView doInBackground(Future<String>... params) {
Future<String> f = params[0];
try {
return f.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String futureResult) {
super.onPostExecute(futureResult);
// this method is run on UI thread
// do something with the result
// or simply hide the progress bar
// if you had previously shown one.
}
}
And run the future thread with:
FutureTask ft = new FutureTask();
ft.execute(future);
Hope this helps.

Categories

Resources