I viewed the answer here: Updating Android UI using threads
But I am not able to understand how to instantiate the handler in the background thread to match the UiThread.
I just want to be clear and say that im using are 2 entirely separate classes.
UiThread Handler Code:
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
if(msg.what==UPDATE_IMAGE){
images.get(msg.arg1).setImageBitmap((Bitmap) msg.obj);
}
super.handleMessage(msg);
}
};
Background Thread Handler code:
if(dataArrives){
Message msg = handler.obtainMessage();
msg.what = UPDATE_IMAGE;
msg.obj = bitmap;
msg.arg1 = index;
handler.sendMessage(msg);
}
In the background class, im getting "handler" as undefined.
Please show the entire thread classes in your answer if you can.
Related
I'm trying to get a hang of multi-threading in Android. My aim is to send data from the main thread to a worker thread.
I have a main activity with the following code in its onCreate-
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ThreadOne threadOne = new ThreadOne();
threadOne.start();
threadOneLooper = threadOne.getLooper();
Message msg = new Message();
msg.obj = "message.";
Log.i("id--(threadOneLooper)", String.valueOf(threadOneLooper.getThread().getId()));
Log.i("id--(Main)", String.valueOf(getMainLooper().getThread().getId()));
TestHandler mHandler = new TestHandler(threadOneLooper);
mHandler.handleMessage(msg);
}
As you can observe, I store a reference of the worker thread's looper in threadOneLooper and then log the id of its associated thread.
At the same time I also print the id of the main thread.
Following is the code for my thread -
public class ThreadOne extends Thread{
#Override
public void run() {
if (Looper.myLooper() == null)
Looper.prepare();
Log.i("ThreadOne", "run()");
// Just making sure all approaches give the same result
Log.i("Id--Thread id 1", String.valueOf(getLooper().getThread().getId()));
Log.i("Id--Thread id 2", String.valueOf(getId()));
Looper.loop();
}
Looper getLooper() {
if (Looper.myLooper() != null)
return Looper.myLooper();
else
return null;
}
And following is the code of my handler -
public class TestHandler extends Handler {
TestHandler(Looper myLooper) {
super(myLooper);
}
public void handleMessage(Message msg) {
String txt = (String) msg.obj;
Log.i("Handler", txt);
Log.i("Id--(Handler)", String.valueOf(getLooper().getThread().getId()));
}
}
Now, the problem is that I had assumed that the Log.i("Id--(Handler)", String.valueOf(getLooper().getThread().getId())); statement would log the thread id of ThreadOne since we are passing the thread's looper into the handler. However, the id being logged is of the main thread. Why is that? Is it correct to assume that handleMessage() is being executed on the main thread?
The problem is with the getLooper() method of ThreadOne. The method returns myLooper() associated with the thread that called the method.
For returning a Looper associated with ThreadOne I'd suggest the following implementation of the class:
public class ThreadOne extends Thread {
private Looper mLooper = null;
#Override
public void run() {
Looper.prepare();
mLooper = Looper.myLooper();
Looper.loop();
}
Looper getLooper() {
return mLooper;
}
}
Note! The ThreadOne's Looper is null until it is running. You cannot get non-null reference to it earlier. You must check if the thread is running before calling the getLooper() method.
P.S. You might want to reconsider the approach with getting the ThreadOne's Looper. Having a Handler associated with ThreadOne might be enough (see the example of thread).
This topic has been discussed many times so far, but still haven't managed got it working.
My MainActivity has a property called Handler mHandlerUi; which is initialized in its constructor
mHandlerUi = new Handler() {
#Override
public void handleMessage(Message msg){
onMessageArrive(msg);
}
};
Later on in the code, In another class, during construction time i initialize another Handler property
mHandlerToUi = new Handler(Looper.getMainLooper());
So during the thread's life time, the following code snippet is executed X times.
Message msg = mHandlerToUi.obtainMessage();
msg.what = ConstMessages.MSG_NEW_GPS_POINT;
msg.setData(bundleContet);
mHandlerToUi.sendMessage(msg);
Unfortunately the message never arrives the MainActivity's Looper, Both threads uses the same UI's looper,
What am i missing over here?
If I understand you correctly, you have initiated two handlers and you want to pass the message from one to another?
Perhaps you should pass the handler to the second class instead. So in your MainActivity you have
mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message inputMessage) {
onMessageArrive(msg);
}
}
and pass this mHandler to your second class via constructor or setter method.
Each Handler handle its messages. The Looper is used for running a Handler on a specific Thread.
If you create two Handler, there will not communicate. There will only share the same Looper that run on a Thread.
You can find good answers on What is the relationship between Looper, Handler and MessageQueue in Android?
While it is useful to run long standing tasks on their own thread running code in the UIThread is necessary in order to update the UI components. Otherwise your application will throw the CalledFromWrongThreadException during execution. How can you run code on the UIThread?
There are a number of ways this can be achieved:
Use runOnUiThread() method call
Use post() method call
Use the Handler framework
Use a Broadcasts and BroadcastReceiver (optionally with LocalBroadcastManager)
Use an AsyncTask's onProgressUpdate() method
Method 1:
runOnUiThread(new Runnable() {
#Override
public void run() {
// do something
}
});
Read more: http://www.intertech.com/Blog/android-non-ui-to-ui-thread-communications-part-1-of-5/#ixzz3hnx3hdS5
Yes you can use handler to communicate between Worker Thread and UI Thread, put below code snippet into worker thread from which you want to update your UI,
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("file", pdfPath);
message.setData(bundle);
handler.sendMessage(message); // pass handler object from activity
put Handler related code into Activity class
Handler handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
String filePath = msg.getData().getString("file"); // You can change this according to your requirement.
}
};
If you aren't familiar with Handler mechanism then first read following link, it will help you
https://developer.android.com/training/multiple-threads/communicate-ui.html
In my application I am employing a service that uses a dictionary loaded from the server. I load the dictionary in a separate thread before calling the service. I want to know how to start the service ONLY after the dictionary has been completely loaded because when i try to access the dictionary inside the service it returns null.
MainActivity.java
onCreate(){
...
doFunction();
Intent intent = new Intent(this, ServiceA.class);
startService(intent);
}
private void doFunction(){
ClassThread().getInstance().process();
}
ClassThread.java
public class ClassThread{
....
public void process(){
final Handler handler = new Handler(new Handler.Callback(){
#Override
public boolean handleMessage (Message msg){
if(msg.arg1 == 1){
loadDictionary();
}
return false;
}
});
final Message message = new Message();
new Thread(){
public void run(){
message.arg1 = 1;
handler.sendMessage(message);
}
}.start();
I can suggest an alternate approach: why not use an AsyncTask instead of a Thread ? Load the dictionary data in doInBackground(), and call startService() in onPostExecute(). No messy thread synchronization required.
The AsyncTask abstraction was designed to be used in conjunction with Android's single UI thread policy in order to avoid precisely these kinds of awkward synchronization problems.
You can use Android's AsynchTask!
i want to send a Broadcast from a FromTowerThread with the following method:
void postToService(final String string){
Handler handler = new Handler();
handler.post(new Runnable(){
#Override
public void run() {
context.sendBroadcast(new Intent(string));
}
});
}
My context is 'this' (my service):
FromTowerThread ftt = new FromTowerThread(this);
(just for completeness my constructor in FromTowerThread):
public FromTowerThread(Context context){
this.context=context;
}
The Error i get is "Can't create handler inside thread that has not called Looper.prepare()".
I don't know how to fix this error, all threads i found about this topic could not help me to understand this.
Thanks in advance!
Chris
change the statement
Handler handler = new Handler();
to
Handler handler = new Handler(Looper.getMainLooper());
Post can only be done from a Handler in a thread that called Looper.prepare().
I'm assuming here that the thread that calls void postToService(final String string) is some type of background thread. Usually the UI thread does call Looper.prepare() meaning that you would have to create the Handler inside the UI thread.
but just as a general suggestion, I'm pretty sure you can just eliminate all of this and just call the broadcast straight away:
void postToService(final String string){
context.sendBroadcast(new Intent(string));
}
The Error i get is "Can't create handler inside thread that has not called Looper.prepare()".
If you are extending Thread, try extending HandlerThread instead. A HandlerThread has a Looper already. (Or you can call Looper.prepare() and Looper.loop() yourself in a regular Thread.)
Otherwise you can do a few things to use the main Thread's Looper. For instance create the Handler in the main Thread and pass it to your worker Thread or even pass a View from the UI Thread and call view.post(Runnable) to execute that Runnable on the UI Thread.