I have a child thread running to do a task infinitely. I want to (1) constantly send data back to the UI thread, and (2) occasionally send data (corresponding to buttons) to the child thread to pause/continue the infinite task. My problem is that the child thread gets stuck in the looper, meaning the task does not execute.
My questions is this: How do I get the child thread to receive messages from the UI thread without blocking the infinite task?
This is what I have so far:
For task (1), I have a handler in my UI thread, which works, and an infinite loop in the child thread that sends back a message, which works by itself.
In UI thread:
mMainHandler = new Handler() {
public void handleMessage(Message msg) {
Bundle b;
b = msg.getData();
if (msg.what==1)
Log.i("main", "from child (running) - " + b.getBoolean("running"));
else if (msg.what == 2)
Log.i("main", "from child (count) - " + b.getInt("count"));
}
};
In child thread (currently using a dummy task until I get the framework worked out):
while (true) {
if (running) {
try {
curCount += up;
if (curCount == maxCount)
up = -1;
else if (curCount == minCount)
up = 1;
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.e("", "local Thread error", e);
}
Bundle b = new Bundle(1);
b.putInt("count", curCount);
Message toMain = mMainHandler.obtainMessage();
toMain.what = 2;
toMain.setData(b);
mMainHandler.sendMessage(toMain);
}
}
For task (2), I have a method in my UI thread corresponding to a button press that sends a message to the child thread, which works, and a handler in the child thread, which works by itself.
In UI thread:
private void sendRunning(boolean running) {
if (mChildHandler != null) {
Bundle b = new Bundle(1);
b.putBoolean("running", running);
Message msg = mChildHandler.obtainMessage();
msg.what = 1;
msg.setData(b);
mChildHandler.sendMessage(msg);
}
}
In child thread:
Looper.prepare();
mChildHandler = new Handler() {
public void handleMessage(Message msg) {
Bundle b;
if (msg.what==1){
b = msg.getData();
running = b.getBoolean("running");
Log.i(INNER_TAG, "from main (running) - " + b.getBoolean("running"));
Log.i(INNER_TAG, "running - " + running);
try {
Message toMain = mMainHandler.obtainMessage();
toMain.what = 1;
toMain.setData(b);
mMainHandler.sendMessage(toMain);
} finally {}
}
}
};
Looper.loop();
Each one of those scenarios works fine alone, but trying to do both at the same time is the problem. If I put the infinite task after the Looper.loop(), it is never reached. If I put it before the Looper.prepare(), it is run once. If I put it withing the looper, it is still only run once.
Any ideas would be greatly appreciated :)
Here is my full code (minus package/imports) for reference:
public class MainActivity extends Activity {
Thread thread;
private Handler mMainHandler, mChildHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMainHandler = new Handler() {
public void handleMessage(Message msg) {
Bundle b;
b = msg.getData();
if (msg.what==1)
Log.i("main", "from child (running) - " + b.getBoolean("running"));
else if (msg.what == 2)
Log.i("main", "from child (count) - " + b.getInt("count"));
}
};
thread = new ChildThread();
thread.start();
// Get a reference to the button
Button buttonStart = (Button)findViewById(R.id.btnStart);
Button buttonStop = (Button)findViewById(R.id.btnStop);
// Set the click listener to run my code
buttonStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,
"Starting...", Toast.LENGTH_SHORT).show();
sendRunning(true);
}
});
buttonStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,
"Stopping...", Toast.LENGTH_SHORT).show();
sendRunning(false);
}
});
}
private void sendRunning(boolean running) {
if (mChildHandler != null) {
Bundle b = new Bundle(1);
b.putBoolean("running", running);
Message msg = mChildHandler.obtainMessage();
msg.what = 1;
msg.setData(b);
mChildHandler.sendMessage(msg);
}
}
#Override
protected void onDestroy() {
Log.i("tag", "stop looping the child thread's message queue");
mChildHandler.getLooper().quit();
super.onDestroy();
}
class ChildThread extends Thread {
private static final String INNER_TAG = "ChildThread";
private boolean running = true;
final int maxCount = 10;
final int minCount = 0;
public int curCount = minCount;
private int up = 1;
public void run() {
while (true) {
if (running) {
try {
curCount += up;
if (curCount == maxCount)
up = -1;
else if (curCount == minCount)
up = 1;
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.e("", "local Thread error", e);
}
Bundle b = new Bundle(1);
b.putInt("count", curCount);
Message toMain = mMainHandler.obtainMessage();
toMain.what = 2;
toMain.setData(b);
mMainHandler.sendMessage(toMain);
}
this.setName("child");
Looper.prepare();
mChildHandler = new Handler() {
public void handleMessage(Message msg) {
Bundle b;
if (msg.what==1){
b = msg.getData();
running = b.getBoolean("running");
Log.i(INNER_TAG, "from main (running) - " + b.getBoolean("running"));
Log.i(INNER_TAG, "running - " + running);
try {
Message toMain = mMainHandler.obtainMessage();
toMain.what = 1;
toMain.setData(b);
mMainHandler.sendMessage(toMain);
} finally {}
}
}
};
Log.i(INNER_TAG, "Child handler is bound to - " +
mChildHandler.getLooper().getThread().getName());
Looper.loop();
}
}
}
}
Just Use Intent service rather then this thread so you can manage all UI of your update and what ever you want to do with UI in intent service one broadcast receiver is using and its very easy to handle threads and infect your your UI is nit hand or lock while your background process run .
I ended up just using a variable time for the thread to avoid the situation. Thanks for the advice.
Related
I have a code running inside the Thread. I tried to use the handler to do receive the message from the thread so i can update the UI. Unfortunately, the message didn't get send to the handler.
This is my code snippet inside the run method of the Thread
ChromaticLayout chromatic = new ChromaticLayout(mPartition, mDeviceWidth, mDeviceHeight, mData);
chromatic.execute(new ChromaticLayout.LayoutCallback() {
#Override
public synchronized void retrieveResult(Object[][] data) {
// TODO Auto-generated method stub
mPhotoData.clear();
Log.w("CALLBACK", "start");
for (int i=0; i<data.length; i++)
{
PhotoFrameData[] row = new PhotoFrameData[data[i].length];
for (int j=0; j<data[i].length; j++) {
if (j==0)
Log.w("CALLBACK", "Width = " + ((PhotoFrameData) data[i][j]).getRectangle().width() + " height = " + ((PhotoFrameData) data[i][j]).getRectangle().height() );
row[j] = (PhotoFrameData) data[i][j];
}
mPhotoData.add(row);
}
Log.w("CALLBACK", "end");
PhotoFrameAdapter.this.handle.post(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
PhotoFrameAdapter.this.handle.sendEmptyMessage(1);
} });
//if (!PhotoFrameAdapter.this.handle.sendEmptyMessage(1))
// Log.w("CALLBACK", "Handle not working");
}});
}
The is the receiving message of the handler:
protected Handler handle = new Handler() {
public void handleMessage(Bundle message) {
//PhotoFrameAdapter.this.notifyDataSetChanged();
mListener.dataLoaded(this);
}
};
What make it not adding to the message queue and call the handleMessage? Thanks
try this:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
//do staff
break;
}
};
};
Another way you can use a Handler is as follows (it's perhaps a simpler implementation in many cases):
Define the hander on the UI thread:
private Handler mHandler = new Handler();
Then from your background thread just post a Runnable with the code you want to run:
mHandler.post(new Runnable() {
#Override
public void run() {
// Code to run here
}
});
I want to set up a HandlerThread from the GUI thread. Then some time later, when a button is clicked on the GUI, it runs callHello(), which then send a message to a HelloLogger object residing on the non-GUI thread which asynchronously logs "Hello World". I have tried a number of things, some block indefinitely, some never receive the message, etc etc. The code below is more or less as close as I have got, please could someone modify it to work?
public class HandlerThreadExample {
private MyHandlerThread mMyHandlerThread;
private Looper mLooper;
private Handler mHandler;
public HandlerThreadExample(){
mMyHandlerThread = new MyHandlerThread();
mMyHandlerThread.start();
mLooper = mMyHandlerThread.getLooper();
}
public void callHello() {
mHandler.sendEmptyMessage(1);
}
private class MyHandlerThread extends HandlerThread {
private HelloLogger mHelloLogger;
private Handler mHandler;
public MyHandlerThread() {
super("The MyHandlerThread thread", HandlerThread.NORM_PRIORITY);
}
public void run (){
mHelloLogger = new HelloLogger();
mHandler = new Handler(getLooper()){
public void handleMessage(Message msg){
mHelloLogger.logHello();
}
};
super.run();
}
}
private class HelloLogger {
public HelloLogger (){
}
public void logHello(){
Log.d("HandlerThreadExample", "Hello World");
}
}
}
Best examples found:
HandlerThread Test
How to create a Looper thread, then send it a message immediately?
Async calls with Handler
HandlerThread vs Executor - When is one more appropriate over the other?
Best use of HandlerThread over other similar classes
Android HandlerThread
HandlerThread examples
Android: Passing data between main and worker threads
Java Synchronised
Sending messages between threads using activity thread queue and Handler class
Intro to Loopers and Handlers
developer.android: Specifying the Code to Run on a Thread
At least now I can close the damned tabs
Solution courtesy of help from pskink
public class HandlerThreadExample2 {
private static int MSG_START_HELLO = 0;
private static int MSG_HELLO_COMPLETE = 1;
private HandlerThread ht;
private Handler mHtHandler;
private Handler mUiHandler;
private boolean helloReady = false;
public HandlerThreadExample2(){
ht = new HandlerThread("The new thread");
ht.start();
Log.d(App.TAG, "UI: handler thread started");
mUiHandler = new Handler(){
public void handleMessage(Message msg){
if (msg.what == MSG_HELLO_COMPLETE){
Log.d(App.TAG, "UI Thread: received notification of sleep completed ");
helloReady = true; }
}
};
mHtHandler = new Handler(ht.getLooper()){
public void handleMessage (Message msg){
if (msg.what == MSG_START_HELLO){
Log.d(App.TAG, "handleMessage " + msg.what + " in " + Thread.currentThread() + " now sleeping");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(App.TAG, "Woke up, notifying UI thread...");
mUiHandler.sendEmptyMessage(MSG_HELLO_COMPLETE);
}
}
};
}
public void sendLongHello(){
if (helloReady){
Log.d(App.TAG, "sending hello " + Thread.currentThread());
mHtHandler.sendEmptyMessage(MSG_START_HELLO);
helloReady = false;
} else {
Log.e(App.TAG, "Cannot do hello yet - not ready");
}
}
}
This is a working example:
HandlerThread ht = new HandlerThread("MySuperAwesomeHandlerThread");
ht.start();
Handler h = new Handler(ht.getLooper()) {
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage " + msg.what + " in " + Thread.currentThread());
};
};
for (int i = 0; i < 5; i++) {
Log.d(TAG, "sending " + i + " in " + Thread.currentThread());
h.sendEmptyMessageDelayed(i, 3000 + i * 1000);
}
UPDATE:
Make two class fields:
Handler mHtHandler;
Handler mUiHandler;
and try this:
HandlerThread ht = new HandlerThread("MySuperAwsomeHandlerThread");
ht.start();
Callback callback = new Callback() {
#Override
public boolean handleMessage(Message msg) {
if (msg.what == 0) {
Log.d(TAG, "got a meaasage in " + Thread.currentThread() + ", now sleeping... ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "woke up, notifying ui thread...");
mUiHandler.sendEmptyMessage(1);
} else
if (msg.what == 1) {
Log.d(TAG, "got a notification in " + Thread.currentThread());
}
return false;
}
};
mHtHandler = new Handler(ht.getLooper(), callback);
mUiHandler = new Handler(callback);
mHtHandler.sendEmptyMessageDelayed(0, 3000);
You can of course get rid of Callback interface and create two Handlers with overridden handleMessage method...
The issue you are seeing is because your outer class is using a private mHandler field and so does your HandlerThread. The outer class' field is not initialized. You don't need the inner mHandler. The outer class can crate a handler from the looper you grab right after calling start().
I am trying to measure the wifi signal strength 5 times (after every second) & display it in a TextView. I simultaneously write it to external storage as well. Everything runs fine except that I am NOT able to see the results in real time. The app would run for 5 secs with a blank screen and then show up the results (which are correct btw, i.e 5 different readings after each second).
I'd want to see the results updating as soon the new value is calculated in each iteration of the for loop.
Thanks
Here is the code
public class WifiDemo extends Activity implements OnClickListener {
private static final String TAG = "WiFiDemo";
WifiManager wifi;
TextView textStatus;
Button buttonScan;
/** Called when the activity is first created. */
/*
* (non-Javadoc)
*
* #see android.app.Activity#onCreate(android.os.Bundle)
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Setup UI
textStatus = (TextView) findViewById(R.id.textStatus);
buttonScan = (Button) findViewById(R.id.buttonScan);
buttonScan.setOnClickListener(this);
// Setup WiFi
wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// Get WiFi status
runOnUiThread(new Runnable() {
public void run() {
try {
FileWriter fw = new FileWriter(
Environment.getExternalStorageDirectory()
+ "/bluetooth/wifi.txt");
for (int i = 0; i < 5; i++) {
WifiInfo info = wifi.getConnectionInfo();
Date d = new Date(System.currentTimeMillis());
String stat = "\n\nWiFi Status: " + info.getRssi()
+ " " + d.getHours() + ":" + d.getMinutes()
+ ":" + d.getSeconds();
textStatus.append(stat);
fw.write(stat);
fw.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
fw.close();
}
catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
textStatus.append("something wrong");
}
}
});
}
}
}
You can try to create a handler to handle UI update tasks in the main thread. Do not update UI in your thread, instead, do it by passing handler messages to make sure this job is handled in the main thread. It works fine for me. I've modified some of your code here (I removed the write file part),
public class WifiDemo extends Activity implements OnClickListener {
private static final String TAG = "WiFiDemo";
private static final int WifiDetectStart = 0;
private static final int WifiDetectStop = 1;
private String stat;
WifiManager wifi;
TextView textStatus;
Button buttonScan;
Handler handler;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Setup UI
textStatus = (TextView) findViewById(R.id.textStatus);
buttonScan = (Button) findViewById(R.id.buttonScan);
buttonScan.setOnClickListener(this);
//setup handler
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
if(msg.what == WifiDetectStart)
{
textStatus.append(stat);
}
if(msg.what == WifiDetectStop)
{
Thread.currentThread().interrupt();
}
super.handleMessage(msg);
}
};
// Setup WiFi
wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// Get WiFi status
Thread myThread = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
WifiInfo info = wifi.getConnectionInfo();
Date d = new Date(System.currentTimeMillis());
stat = "\n\nWiFi Status: " + info.getRssi()
+ " " + d.getHours() + ":" + d.getMinutes()
+ ":" + d.getSeconds();
Message msg = new Message();
msg.what = WifiDetectStart;
handler.sendMessage(msg);
// textStatus.append(stat);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//finish this operation
Message msg = new Message();
msg.what = WifiDetectStop;
handler.sendMessage(msg);
}
});
myThread.start();
}
}
The problem is you're doing something right by trying to do your updating in a separate Runnable... however, your Runnable is running in the UI thread and therefore causing the UI thread to sit in the loop (including the Thread.sleep()). You're not getting your updates because you're causing the UI to wait on you.
If your processing is reasonably heavy, you might wish to break it out into a separate thread and send messages to a handler. Otherwise, it might be easiest to do something like the following (untested, but something similar):
if (Environment.MEDIA_MOUNTED.equals(state)) {
textStatus.postDelayed(new Runnable() {
public void run() {
WifiInfo info = wifi.getConnectionInfo();
Date d = new Date(System.currentTimeMillis());
String stat = "\n\nWiFi Status: " + info.getRssi()
+ " " + d.getHours() + ":" + d.getMinutes()
+ ":" + d.getSeconds();
textStatus.append(stat);
// relaunch if we're not through with our number of iterations.
// mCount is a new field.
if(mCount++ < 5) {
textStatus.postDelayed(this, 1000);
}
}
}, 1000);
}
can anybody tell me,
how can get data(message) from message queue ?
or how can send message from main thread to other thread ?.
Thanks
If you want to receive messages on a thread you should run a Looper and create a message Handler bound to this looper. The UI thread has a looper by default. There's a convenient class for creating threads with loopers called HandlerThread. Here's a good article about Handlers and Loopers: Android Guts: Intro to Loopers and Handlers.
EDIT:
HandlerThread thread = new HandlerThread("Thread name");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(looper) {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case SOME_MESSAGE_ID:
// SOME_MESSAGE_ID is any int value
// do something
break;
// other cases
}
}
};
handler.post(new Runnable() {
#Override
public void run() {
// this code will be executed on the created thread
}
});
// Handler.handleMessage() will be executed on the created thread
// after the previous Runnable is finished
handler.sendEmptyMessage(SOME_MESSAGE_ID);
not any other Thread.. you can sen it main Thread or Ui thread, using Handler..
create handler and send a runnable object as anarguement..
When an application first starts on a device
private final Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
if (WHAT == msg.what) {
textView.setMaxLines(msg.arg1);
textView.invalidate();
} else if (WHAT_ANIMATION_END == msg.what) {
setExpandState(msg.arg1);
} else if (WHAT_EXPAND_ONLY == msg.what) {
changeExpandState(msg.arg1);
}
super.handleMessage(msg);
}
};
You can use various technique to create a thread in android.
private void doAnimation(final int startIndex, final int endIndex, final int what) {
thread = new Thread(new Runnable() {
#Override public void run() {
if (startIndex < endIndex) {
// if start index smaller than end index ,do expand action
int count = startIndex;
while (count++ < endIndex) {
Message msg = handler.obtainMessage(WHAT, count, 0);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(msg);
}
} else if (startIndex > endIndex) {
// if start index bigger than end index ,do shrink action
int count = startIndex;
while (count-- > endIndex) {
Message msg = handler.obtainMessage(WHAT, count, 0);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(msg);
}
}
// animation end,send signal
Message msg = handler.obtainMessage(what, endIndex, 0);
handler.sendMessage(msg);
}
});
thread.start();
}
Please note: usually prepare() and loop() are already called in the Activity, so we should perform the following check:
if (Looper.myLooper() == null) {
Looper.prepare();
}
Consider i have one thread as a separate class , for example SimpleThread.java,
class SimpleThread extends Thread {
public SimpleThread(String str) {
super(str);
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
System.out.println("DONE! " + getName());
}
}
from my android home.java i need to start the thread,
new SimpleThread("Jamaica").start();
once the loop end i need to shoe the alert,but when i use
authalert = new AlertDialog.Builder(this);
it shows null pointer execption, i need a context over here in thread class , is there any other way to do this.
Hey you should use Handler for this
here is the code ...
ProgressDialog _progressDialog = ProgressDialog.show(this,"Saving Data","Please wait......");
settintAdater();
private void settingAdater(){
Thread _thread = new Thread(){
public void run() {
Message _msg = new Message();
_msg.what = 1;
// Do your task where you want to rerieve data to set in adapet
YourCalss.this._handle.sendMessage(_msg);
};
};
_thread.start();
}
Handler _handle = new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case 1:
_progressDialog.dismiss();
listview.setAdapter();
}
}
}
One way of solving your problem is using Handlers, as Sujit suggested. Other way is using AsyncTask. Read here.
the problem is : when you launch the thread, the Compiler will not wait until the thread finish his treatement , he will execute the next instruction ( authalert = new AlertDialog.Builder(this); )
so there are two or three ways to do this :
1) , use handler
2) define your own listener for your thread in order to listen until he finished his treatement ,
3) you can pass the Context of your activity , and at the last line of your run method , display the AlertDialog ( with Activity.runOnUiThread(new Runnable); )
You should read http://www.aviyehuda.com/2010/12/android-multithreading-in-a-ui-environment/ and http://developer.android.com/resources/articles/painless-threading.html
one way would be put a handler in your calling activity:
final mContext=this;
final Handler mHandler=new Handler(){
#Override
public void handleMessage(Message msg) {
int yourIntReturnValue=msg.what;
//cast your object back to whatever it was lets say it was a string:
// String yourString=(String) msg.obj;
//do something like authalert = new AlertDialog.Builder(mContext);
}
};
then
class SimpleThread extends Thread {
Handler mHandler;
public SimpleThread(String str, Handler h) {
super(str);
mHandler=h;
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
System.out.println("DONE! " + getName());
Message.obtain(mHandler, someIntRetValue,
"DONE" ).sendToTarget();
}
}