Send message to UI thread - java

I create a thread to listening the input stream of the serial port.
And when there is input data coming, I want to send a message to UI thread.
Before doing my task, I try to send message from the thread with interval 5000 ms, and send a message to UI thread and Toast it.
From the log, I know that the thread is created and running succefully, but the number is not toasted in the UI thread.
I think it may be due to fail to send a message from the thread to UI thread, or fail to handle the message in the MessageQueue of UI thread.
What's wrong with my code?
Many thanks!
MainActivity.java:
public class MainActivity extends Activity {
...
#Override
protected void onCreate(Bundle savedInstancesState) {
...
setMessageProcessor();
SerialPortListener.start();
}
private void setMessageProcessor() {
new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this,
"Message: " + msg.obj.toString(),
Toast.LENGTH_SHORT).show();
}
};
}
}
SerialPortListener.java:
public class SerialPortListener {
...
private static int testNumber = 1;
public static void start() {
...
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Message msg = new Message();
msg.obj = testNumber++;
new Handler(Looper.getMainLooper()).sendMessage(msg);
Log.i("TEST", "TEST);
try {
Thread.sleep(5000);
} catche(Exception e) {
...
}
}
}
});
thread.start();
}
}

It's because you are creating a second Handler bound to the main thread from within your "reader thread". Don't use an anonymous object for the handler in the main thread, make it a member field. Then in your reader thread, send the Message to that specific Handler:
private void setMessageProcessor() {
mHandler = new Handler() {
...
}
}
public class SerialPortListener {
...
private static int testNumber = 1;
public static void start() {
...
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Message msg = mHandler.obtainMessage();
msg.obj = testNumber++;
msg.sendToTarget();
Log.i("TEST", "TEST);
...
}
}
}

Related

RunOnUiThread can't access Message directly

I'm sending a message to a Handler which is associated
to it's own Thread.
In the handleMessage method I try to update the UI with the content of
the message using runOnUiThread.
This works fine if take the message obj parameter from handleMessage
and assign it to a new final variable.
But if I don't use this assignment and take msg.obj directly in the runnable
the obj variable is null even though the msg reference is having the same id
when checking the msg reference passed to handleMessage before calling runOnUiThread.
Why is that happening?
This is working:
bt01.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Message msg = new Message();
msg.obj = new Data("Hi im the message");
mHandler.sendMessage(msg);
}
});
class LooperThread extends Thread {
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(final Message msg) {
final Object messageString = msg.obj;
runOnUiThread(new Runnable() {
#Override
public void run() {
tv01.setText(((Data)messageString).getMessage());
}
});
}
};
Looper.loop();
}
}
This is not working:
public void handleMessage(final Message msg) {
runOnUiThread(new Runnable() {
#Override
public void run() {
tv01.setText(((Data)msg.obj).getMessage());
}
});
}
My guess is that the operating system is reusing these Message objects, rather than creating new ones. By the time your Runnable starts running on the UI thread, the Message has been returned to the pool and its fields have been set to null. The existence of obtainMessage() supports this hypothesis.

Updating UI inside a thread (Android Studio/JAVA)

I am creating a small game over wifi-direct through sockets on Android. On the client receiving thread I am listening for Messages from the other device:
class ReceivingThread implements Runnable {
#Override
public void run() {
BufferedReader input;
try {
input = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
while (!Thread.currentThread().isInterrupted()) {
final String messageStr = input.readLine();
if (messageStr != null) {
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, messageStr, Toast.LENGTH_LONG).show();
activity.handleMessage(messageStr);
}
});
} else {
break;
}
}
input.close();
} catch (IOException e) {
}
}
}
And the toast shows the message just fine, but the app crashes on activity.handleMessage(). This is what the method does (cut it down just to one case, just for the point of the method):
public void handleMessage(String msg){
switch(msg){
case "1":
Button temp = (Button) findViewById(btn1);
if(isOwner) {
temp.setText("O");
} else {
temp.setText("X");
}
temp.setEnabled(false);
break;
case "You Lose":
TextView textBox = (TextView) findViewById(R.id.textBox);
textBox.setText("You Lose!");
}
}
And the message I get is:
java Looper.prepare() wasn't called on this thread.
Instead of activity.handleMessage(messageStr); you need to send it as a message to the handler, so handleMessage will execute in the loop.
Bundle data = new Bundle();
data.putString("MESSAGE_KEY", messageStr);
Message msg = new Message();
msg.setData(data);
activity.sendMessage(msg);
So you will receive in handleMessage of the Handler the Message, to extract the String you do message.getData().getString("MESSAGE_KEY");
Create UI thread inside handler and try to perform the UI operation.
new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message message) {
// This is where you do your work in the UI thread.
// Your worker tells you in the message what to do.
}
};

postDelayed() with messages Android

I want my main UI thread to effectively "sleep" for 1 second before sending an empty message to a worker thread to perform some operation.
Sleep() is a problem for me because I cannot quit() the threads properly while it is performing sleep(), so I want to change it to postDelayed(runnable r, long msDelay) but it takes runnable object, not Message. How can I change this code? I am just passing empty message from main UI thread to worker thread.
UI thread:
public class MainActivity extends Activity
{
static Worker w1, w2;
static Handler mainUIHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
String sender = msg.getData().getString("SENDER");
if (Objects.equals(sender, "CPU1"))
{
mInfoTextView.setText("worker 1 thinking...");
//how to change the following 2 lines to postDelayed()?
try{
Thread.sleep(1000);
} catch (Exception ignored) { }
w2.handler.sendMessage(w2.handler.obtainMessage());
}
}
};
...
}
and the worker thread
class Worker extends HandlerThread
{
...
Worker(String name)
{
super(name);
}
Handler handler= new Handler()
{
#Override
public void handleMessage(Message msg)
{
//perform work
Foo work = dowork();
//communicate work to the UI thread to update the display
Message message = MainActivity.mainUIHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("NAME", name);
bundle.putInt("work", work);
message.setData(bundle);
MainActivity.mainUIHandler.sendMessage(message);
}
};
#Override
public void run()
{
this.setName(WorkerThread.class.getName());
this.setPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
}
}
I tried creating a new Runnable() and passing it to postDelayed():
w2.handler.postDelayed(new Runnable()
{
#Override
public void run()
{
System.out.println("hello");
}
}, 1000);
but it's not triggering the handler in w2
You can delay a Message with Handler.sendMessageDelayed():
public class MainActivity extends Activity {
// ...
static Handler mainUIHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// ...
w2.handler.sendMessageDelayed(w2.handler.obtainMessage(), 1000);
}
}
}

Android HTTP: Make more than 1 asynctask request

I followed this post to set up an Http Async Request: HttpRequest
So, now, I call: new DownloadTask().execute("http://www.google.com/");
to make this request.
How can I manage different calls? For example:
new DownloadTask().execute("http://www.google.com/");
new DownloadTask().execute("http://www.facebook.com/");
new DownloadTask().execute("http://www.twitter.com/");
And have different results?
Pass one more argument to the AsyncTask. Make some constants corresponding to your tasks.
new DownloadTask().execute("http://www.google.com/", DownloadTask.ID_ASYNC1);
new DownloadTask().execute("http://www.facebook.com/", DownloadTask.ID_ASYNC2);
new DownloadTask().execute("http://www.twitter.com/", DownloadTask.ID_ASYNC3);
Inside AsyncTask, use this id to identify which is the request being called.
private class DownloadTask extends AsyncTask<String, Void, String> {
//Variable for storing the req id
private int id;
//Constants corresponding to your tasks
public static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
#Override
protected String doInBackground(String... params) {
id = params[1]);
//your code
}
#Override
protected void onPostExecute(String result) {
if(id == ID_ASYNC1){
//Do your task #1
} else if(id == ID_ASYNC2){
//Do your task #2
}
}
}
You have to use looper for smooth downloading for multiple file it will download them one by one. In this way your application run smoothly huge huge amount of downloads.
How to use Looper
public class MainActivity extends Activity implements DownloadThreadListener,
OnClickListener {
private DownloadThread downloadThread;
private Handler handler;
private ProgressBar progressBar;
private TextView statusText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create and launch the download thread
downloadThread = new DownloadThread(this);
downloadThread.start();
// Create the Handler. It will implicitly bind to the Looper
// that is internally created for this thread (since it is the UI
// thread)
handler = new Handler();
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
statusText = (TextView) findViewById(R.id.status_text);
Button scheduleButton = (Button) findViewById(R.id.schedule_button);
scheduleButton.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
// request the thread to stop
downloadThread.requestStop();
}
// note! this might be called from another thread
#Override
public void handleDownloadThreadUpdate() {
// we want to modify the progress bar so we need to do it from the UI
// thread
// how can we make sure the code runs in the UI thread? use the handler!
handler.post(new Runnable() {
#Override
public void run() {
int total = downloadThread.getTotalQueued();
int completed = downloadThread.getTotalCompleted();
progressBar.setMax(total);
progressBar.setProgress(0); // need to do it due to a
// ProgressBar bug
progressBar.setProgress(completed);
statusText.setText(String.format("Downloaded %d/%d", completed,
total));
// vibrate for fun
if (completed == total) {
((Vibrator) getSystemService(VIBRATOR_SERVICE))
.vibrate(100);
}
}
});
}
#Override
public void onClick(View source) {
if (source.getId() == R.id.schedule_button) {
int totalTasks = new Random().nextInt(3) + 1;
for (int i = 0; i < totalTasks; ++i) {
downloadThread.enqueueDownload(new DownloadTask());
}
}
}
}
DownloadThread.Class
public final class DownloadThread extends Thread {
private static final String TAG = DownloadThread.class.getSimpleName();
private Handler handler;
private int totalQueued;
private int totalCompleted;
private DownloadThreadListener listener;
public DownloadThread(DownloadThreadListener listener) {
this.listener = listener;
}
#Override
public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();
Log.i(TAG, "DownloadThread entering the loop");
// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();
// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
Log.i(TAG, "DownloadThread exiting gracefully");
} catch (Throwable t) {
Log.e(TAG, "DownloadThread halted due to an error", t);
}
}
// This method is allowed to be called from any thread
public synchronized void requestStop() {
// using the handler, post a Runnable that will quit()
// the Looper attached to our DownloadThread
// obviously, all previously queued tasks will be executed
// before the loop gets the quit Runnable
handler.post(new Runnable() {
#Override
public void run() {
// This is guaranteed to run on the DownloadThread
// so we can use myLooper() to get its looper
Log.i(TAG, "DownloadThread loop quitting by request");
Looper.myLooper().quit();
}
});
}
public synchronized void enqueueDownload(final DownloadTask task) {
// Wrap DownloadTask into another Runnable to track the statistics
handler.post(new Runnable() {
#Override
public void run() {
try {
task.run();
} finally {
// register task completion
synchronized (DownloadThread.this) {
totalCompleted++;
}
// tell the listener something has happened
signalUpdate();
}
}
});
totalQueued++;
// tell the listeners the queue is now longer
signalUpdate();
}
public synchronized int getTotalQueued() {
return totalQueued;
}
public synchronized int getTotalCompleted() {
return totalCompleted;
}
// Please note! This method will normally be called from the download
// thread.
// Thus, it is up for the listener to deal with that (in case it is a UI
// component,
// it has to execute the signal handling code in the UI thread using Handler
// - see
// DownloadQueueActivity for example).
private void signalUpdate() {
if (listener != null) {
listener.handleDownloadThreadUpdate();
}
}
}
DownloadTask.Class
public class DownloadTask implements Runnable {
private static final String TAG = DownloadTask.class.getSimpleName();
private static final Random random = new Random();
private int lengthSec;
public DownloadTask() {
lengthSec = random.nextInt(3) + 1;
}
#Override
public void run() {
try {
Thread.sleep(lengthSec * 1000);
// it's a good idea to always catch Throwable
// in isolated "codelets" like Runnable or Thread
// otherwise the exception might be sunk by some
// agent that actually runs your Runnable - you
// never know what it might be.
} catch (Throwable t) {
Log.e(TAG, "Error in DownloadTask", t);
}
}
}
DownloadThreadListener.class (interface)
public interface DownloadThreadListener {
void handleDownloadThreadUpdate();
}
Using this you can add huge amount of downloads it will add them queue.
Complete tutorial

Calling Method w/ Argument on Main Thread from Secondary Thread

Like the title suggest I have an android project with a MainActivity class that has a TextView that I want to set the text of after receiving a message. I also have a class that runs a ServerSocket on a separate thread that receives the string message I want to display.
Part of my MainActivity looks like this,
private Handler UIHandler = new Handler();
private RemoteControlServer remoteConnection;
public static final int controlPort = 9090;
public class MainActivity extends Activity implements SensorEventListener
{
...
remoteConnection = new RemoteControlServer(controlPort, UIHandler);
...
private class RemoteControlServer extends RemoteControl
{
RemoteControlServer(int port, Handler ui)
{
super(port, ui);
}
#Override
public void onReceive(String[] msg)
{
//updates messages textview
}
#Override
public void onNotify(String[] msg)
{
//updates notification textview
}
}
}
The RemoteControlServer implementation of code that calls the onReceive(String[] msg) and also handles receiving messages on the different thread looks like this,
...
public abstract void onReceive(String[] msg);
public abstract void onNotify(String[] msg);
...
controlListener = new Thread(new Runnable()
{
boolean running = true;
public void run()
{
String line = null;
while(running)
{
try
{
//Handle incoming messages
...
onReceive(messages);
}
catch (final Exception e)
{
UIHandler.post(new Runnable()
{
public void run()
{
onNotify("Wifi Receive Failed " + e.toString() + "\n");
}
});
}
}
}
});
...
I'm getting the error "Only the original thread that created a view hierarchy can touch its views." when onReceive() is called and throws the exception and calls onNotify() with the exception description. Why does the onNotify() work but the otherone does not? How can I correctly call the listener to the the TextView and update its text? Thanks
private class RemoteControlServer extends RemoteControl
{
...
public class BridgeThread implements Runnable
{
String[] msgArray = null;
public BridgeThread(String[] msg)
{
msgArray = msg;
}
public void run()
{
runOnUiThread(new Runnable()
{
#Override
public void run()
{
TextView zValue = (TextView) findViewById(R.id.connectionStatus);
zValue.setText(msgArray[0]);
}
});
}
}
#Override
public void onReceive(String[] msg)
{
BridgeThread bridgeTest = new BridgeThread(msg);
bridgeTest.run();
}
...
}

Categories

Resources