How to update UI via Handler - android

So, I am getting an error that I am updating the UI from the wrong thread. This of course was not my intention. My case is quite long, but I will try to do it justice with code snippets. My end goal is to run an expensive task in a separate thread and post update that happen along the way and at the end to my listView.
public class test extends Activity {
private ArrayAdapter<String> _mOutArrayAdapter;
private ListView _mOutView;
private EditText _mCmdEditText;
private Button _mRunButton;
private Interpreter _interpreter;
// Need handler for callbacks to the UI thread
public final Handler _mHandler = new Handler() {
public void handleMessage(Message msg) {
_mOutArrayAdapter.add(msg.getData().getString("text"));
};
};
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_interpreter = new Interpreter(true);
_mOutView = (ListView)findViewById(R.id.out);
_mCmdEditText = (EditText)findViewById(R.id.edit_command);
_mRunButton = (Button)findViewById(R.id.button_run);
_mOutArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
_mOutView.setAdapter(_mOutArrayAdapter);
_mOutArrayAdapter.clear();
_interpreter.setOutputAdapter(_mOutArrayAdapter);
Thread t = new Thread() {
public void run() {
_mResults = _interpreter.executeExpression("startup;",_mHandler);
_mHandler.post(mUpdateResults);
}
};
t.start();
);
And then inside inpterpreter I do this:
public class Interpreter
{
private static Handler _mHandler;
public String executeExpression(String expression, Handler handler)
{
_mHandler = handler;
//Do a bunch of stuff that sometimes calls displayText from this class or from others
return answer;
}
public void displayText(String text)
{
Message msg = new Message();
Bundle bndl = new Bundle();
bndl.putString("text", text);
msg.setData(bndl);
_mHandler.dispatchMessage(msg);
}
}
The display of the final answer works. And the dispatchMessage is ending up triggering handleMessage, but it throw an error that I cannot modify the UI from outside of the UI thread which I know is illegal. So, what am I doing wrong?
Thanks!

_mHandler.dispatchMessage(msg);
dispatchMessage() causes the Handler to be run on the current thread.
http://developer.android.com/reference/android/os/Handler.html
dispatchMessage(Message msg)
Handle system messages here.
You should be using _mHandler.sendMessage(msg); It will put the message on the queue to be run by the Thread that declared the Handler.
sendMessage(Message msg)
Pushes a message onto the end of the message queue after all pending messages before the current time.

I would strongly suggest you stick with an AsyncTask (or one of the droid-fu versions if you need rotation/background support) unless you know what you're getting into. It'll help you cleanly keep track of what code is running in your UI thread and what code is in the background task, and save you a lot of confusion that dealing with Threads and Handlers yourself can cause.

Handler's post method requires a Runnable object in parameter, and scheduling execution of that runnable block. Instead you can use Handler.sendEmptyMessage() or Handler.sendMessage() to send a message to Handler. SO change your code to following:
Thread t = new Thread() {
public void run() {
_mResults = _interpreter.executeExpression("startup;",_mHandler);
Message msg= _mHandler.obtainMessage();
msg.obj= _mResults;
_mHandler.sendMessage(msg);
}
};

Related

How can I run a specific type of task off the UI thread?

I want to run a function whenever a recyclerview binds a view. It's a really long operation, so I have to keep it off the UI thread. I know how to create a thread, but how can I use the same thread to always run the content of the onBindViewHolder method on the second thread without creating a new one every time the specific function gets executed ?
#Override
public void onBindViewHolder (final Adapter adapter, RecyclerView.ViewHolder holder, final int position) {
new Thread(new Runnable() {
#Override
public void run () {
theMethod(adapter, holder, position);
}
}).start();
}
Consider using a HandlerThread, then scheduling work against that thread with a Handler created with the HandlerThread's Looper. You will have to remember the shut down the thread at the end of the activity, of course, or it will stick around indefinitely and maybe leak objects.
For example:
HandlerThread thread = new HandlerThread("Give Me a Name");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(mServiceLooper);
// use handler to post work to the thread
These lines of code are cribbed directly from IntentService, which does exactly the same thing you want, except in a service. Maybe that's even what you actually want to use!
Create new thread which can do jobs with a queue.
class YourThread extends Thread {
public List<Job> queue = new ArrayList<>();
#Override
public void run(){
while(queue.size() == 0)
wait(); // nothing to do, wait...
Job job = queue.get(queue.size() - 1); // pop queue
queue.remove(queue.size() - 1);
dosomething(job);
}
public void addJob(....){
...
queue.add(job, 0);
}
}
and onBindViewHolder
YourThread thread = new ...
thread.start()
#Override
public void onBindViewHolder (final Adapter adapter, RecyclerView.ViewHolder holder, final int position) {
thread.addJob(...);
// remember that after the job finished, the item may be recycled
}
You can achieve it by using Looper. The main purpose of Looper is this only, to keep the thread alive and waiting for new messages(Runnables).
Here's a sample code from the official documentation :
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Now, you can post new runnables (messages) to the thread using the handler. Something like this :
handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
}
});
Edit :
The method provided above is more of 'do-it-yourself'. There's a special class in android that can do this automatically for you, i.e. HandlerThread. Sample code :
HandlerThread thread = new HandlerThread("name");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(looper);
Now you can post runnables to this handler.

Passing a handler from a background Handler Thread, to background thread

Can anyone point me in the right direction here please ?
I have an activity which spawns two threads, a thread for handling messages, using a Looper
public static class MiddleThread extends Handler{
static public Handler handler;
public void run() {
Looper.prepare();
Log.d("MiddleThread", "Looper is prepared !");
handler = new Handler() {
public void handleMessage(Message msg)
{
Bundle bundle = msg.getData();
String exitString = bundle.getString("endmessage");
if(exitString.equals(("ExitOK")))
{
boolean searchFinished = true;
Looper looper = Looper.myLooper();
looper.quit();
} else
{
int fileCount = bundle.getInt("filecount");
String fileName = bundle.getString("filename");
Log.d("MiddleThread", "File Number " + fileCount + " is " + fileName);
}
}
};
Log.d("MiddleThread", "nandler should be initialised");
Looper.loop();
}
... then it spawns the main Worker Thread, which is passed a handler from the UI Thread, and the handler from the above thread.
public class BasicSearch {
public Handler handlerUi, handlerMiddleThread;
public Message messageUi, messageMiddleThread;
public int fileCount = 0;
public BasicSearch(Handler ui, Handler mt) {
handlerUi = ui;
handlerMiddleThread = mt;
}
public void listFiles()
{
File searchPath = Environment.getExternalStorageDirectory();
messageUi = handlerUi.obtainMessage();
messageMiddleThread = handlerMiddleThread.obtainMessage();
walk(searchPath);
Bundle b = new Bundle();
b.putString("endmessage", "ExitOK");
messageMiddleThread.setData(b);
handlerMiddleThread.dispatchMessage(messageMiddleThread);
}
private void walk(File path) {
File[] list = path.listFiles();
for(File f : list)
{
if(f.isDirectory())
{
walk(new File(f.getAbsolutePath()));
} else {
processFile(f);
}
}
}
private void processFile(File f) {
Bundle b = new Bundle();
fileCount++;
b.putString("filename", f.getName());
b.putInt("filecount", fileCount);
messageMiddleThread.setData(b);
Log.d("BasicSearch", "Data is set, to send to MiddleThread");
handlerMiddleThread.dispatchMessage(messageMiddleThread);
Log.d("BasicSearch", "Message sent");
}
}
Whatever happens, when it tries to dispatchMessage, handlerMiddleThread reverts to being null. I even have the following code in my activity, to try and ensure that it isn't null, but it still ends up being null when I get to send the message.
startMiddleThread();
while(true)
{
if(MiddleThread.handler != null)
break;
}
startSearchThread();
This is a test project, as I wanted to be able to get the Handler/Looper concept properly understood before continuing on with my project.
I have successfully managed to use a Handler in my UI Threads before, but my current project has too much processing going on in the UI, and I want to have a secondary thread handling the output from the searchThread, and just receive a message in UI thread when the thread is complete.
So I think I see what you're trying to do and let me suggest a slightly easier way:
To start your background thread and get a handler to it:
HandlerThread bgThread = new HandlerThread();
bgThread.start();
Handler bgHandler = new Handler(bgThread.getLooper());
Then you can send whatever messages you want to your bgHandler. Note that you need to call start on a HandlerThread before creating the bgThread (otherwise getLooper() will return null).
That being said I think I know whats wrong with your code as you posted it. First, MiddleThread extends Handler (which doesn't have a run() method!) not Thread. Second, the run() method on MiddleThread is never called, so Handler is never instantiated. Even if your just mistyped Handler in your code above and you're actually extending Thread, you still need to call start on MiddleThread in order for anything in run() to be executed. Really though, what you're doing is waaay more complicated that it needs to be, and you almost certainly want to just do what I mentioned above.

Update textView from thread

In my OnCreate method I have created a thread that listens to incoming message!
In OnCreate() {
//Some code
myThread = new Thread() {
#Override
public void run() {
receiveMyMessages();
}
};
myThread.start();
// Some code related to sending out by pressing button etc.
}
Then, receiveMyMessage() functions…
Public void receiveMyMessage()
{
//Receive the message and put it in String str;
str = receivedAllTheMessage();
// << here I want to be able to update this str to a textView. But, How?
}
I checked this article but it did not work for me, no luck!
Any updates to the UI in an Android application must happen in the UI thread. If you spawn a thread to do work in the background you must marshal the results back to the UI thread before you touch a View. You can use the Handler class to perform the marshaling:
public class TestActivity extends Activity {
// Handler gets created on the UI-thread
private Handler mHandler = new Handler();
// This gets executed in a non-UI thread:
public void receiveMyMessage() {
final String str = receivedAllTheMessage();
mHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely modify Views
mTextView.setText(str);
}
});
}
The AsyncTask class simplifies a lot of the details for you and is also something you could look into. For example, I believe it provides you with a thread pool to help mitigate some of the cost associated with spawning a new thread each time you want to do background work.
Android supports message-passing concurrency using handlers and sendMessage(msg). (It is also possible to use handlers for shared-memory concurrency.) One tip is to call thread.setDaemon(true) if you wish the thread to die when the app dies. The other tip is to have only one handler and use message.what and a switch statement in the message handler to route messages.
Code and Code

A common class for asynctask UI handling

I have used a lot of asynctask class in my application. Is it possible to write common class and update the user interface value?
As long as Java has no closures, I don't think this makes a lot of sense.
If you are always doing the same task and only want to modify different UI elements, you can go and pass them in a constructor and then later modify them in onPostExecute().
Instead of using async tasks you can post messages to a common handler to handle UI messages
Common UI handler
private Handler messageHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
//handle update
//.....
}
}
};
Thread to post the message
Thread t = new Thread() {
public void run() {
while (true) {
mResults = doSomethingExpensive();
//Send update to the main thread
messageHandler.sendMessage(Message.obtain(messageHandler, mResults));
}
}
};
t.start();
}

MultiThreading issues while programing for android

I am developing on Android but the question might be just as valid on any other Java platform.
I have developed a multi-threaded app. Lets say I have a first class that needs to do a time-intensive task, thus this work is done in another Thread.
When it's done that same Thread will return the time-intensive task result to another (3rd) class.
This last class will do something and return it's result to the first-starting class.
I have noticed though that the first class will be waiting the whole time, maybe because this is some kind of loop ?
Also I'd like the Thread-class to stop itself, as in when it has passed it's result to the third class it should simply stop. The third class has to do it's work without being "encapsulated" in the second class (the Thread one).
Anyone knows how to accomplish this ?
right now the experience is that the first one seems to be waiting (hanging) till the second and the third one are done :(
If you want to use threads rather than an AsyncTask you could do something like this:
private static final int STEP_ONE_COMPLETE = 0;
private static final int STEP_TWO_COMPLETE = 1;
...
private doBackgroundUpdate1(){
Thread backgroundThread = new Thread() {
#Override
public void run() {
// do first step
// finished first step
Message msg = Message.obtain();
msg.what = STEP_ONE_COMPLETE;
handler.sendMessage(msg);
}
}
backgroundThread.start();
}
private doBackgroundUpdate2(){
Thread backgroundThread = new Thread() {
#Override
public void run() {
// do second step
// finished second step
Message msg = Message.obtain();
msg.what = STEP_TWO_COMPLETE;
handler.sendMessage(msg);
}
}
backgroundThread.start();
}
private Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch(msg.what){
case STEP_ONE_COMPLETE:
doBackgroundUpdate2();
break;
case STEP_TWO_COMPLETE:
// do final steps;
break;
}
}
}
You would kick it off by calling doBackgroundUpdate1(), when this is complete it sends a message to the handler which kicks off doBackgroundUpdate2() etc.
Tiger ,
TiGer wrote:
When it's done that same Thread will
return the time-intensive task result
to another (3rd) class
Since thread runs asynchronously so your non-thread class can't be synced with your thread
Though to perform some action on an Activity you need an AsyncTask not A Thread
TiGer wrote:
maybe because this is some kind of
loop ?
Tiger do read more about Threads and concurrency
So the only answer I have for you now is ASYNCTASK
EDIT:
Also I'd like the Thread-class to stop
itself
Read this post's how-do-you-kill-a-thread-in-java
In ordinary Java, you would do this:
class MyTask implements Runnable {
void run() {
for (int i = 0; i < Integer.MAX; i++) {
if (i = Integer.MAX -1) {
System.out.println("done");
}
}
}
}
class MyMain {
public static void main(String[] argv) {
for (int i = 0; i < 10; i++) {
Thread t = new Thread(new MyTask());
t.start();
}
System.out.println("bye");
}
}
... that kicks off 10 threads. Notice that if you accidentally invoke t.run() instead of t.start(), your runnable executes in the main thread. Probably you'll see 'bye' printed before 10 'done'. Notice that the threads 'stop' when the the run() method of the Runnable you gave to them finishes.
I hope that helps you get your head around what it is you've got to co-ordinate.
The tricky part with concurrency is getting threads to communicate with each other or share access to objects.
I believe Android provides some mechanism for this in the form of the Handler which is described in the developer guide under designing for responsiveness.
An excellent book on the subject of concurrency in Java is Java Concurency in Practice.
if you want use AsyncTask rather then thread in android
I have resolve it using ASyncTask and Handler in Android the aim is that one task is execute after compilation of one task hear is code that show First load animation on view after compilation of that process it will goes on another page
class gotoparent extends AsyncTask<String,String,String>
{
#Override
protected String doInBackground(String... params) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Animation animation= AnimationUtils.loadAnimation(getApplicationContext(),R.anim.rotete);
lin2.startAnimation(animation);
}
});
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent i=new Intent(getApplicationContext(),ParentsCornor.class);
startActivity(i);
}
}, 1200);
}
}

Categories

Resources