Accessing UI Thread Handler from a Service

Updating UI from a service (using a handler?)

My 1st instinct is that you should instead have the Activity bind to your service and handle the UI update on its side instead of the Service directly modifying the Activity.

See more info here:

http://developer.android.com/reference/android/app/Service.html#LocalServiceSample

And an example here:

Example: Communication between Activity and Service using Messaging

Why does runOnUiThread() not work inside Service?

So there's no need to do that, unless you're creating a Thread inside
of it

Gabe Sechan's answer is correct.

But if you are using a separate thread then instead of following code:

new MainActivity().runOnUiThread(new Runnable() {
public void run() {
OverviewFragment.refresh(getApplicationContext());
System.out.println("yay");

}
});

Try, this code:

new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
OverviewFragment.refresh(getApplicationContext());
System.out.println("yay");
}
});

As per Android docs

Caution: A service runs in the main thread of its hosting process—the
service does not create its own thread and does not run in a separate
process (unless you specify otherwise).

Accessing UI component on Activity from the Service class's Thread in Android

And how can I get access to the Activity components from the run method of Thread class?

You don't. Use Messenger to send Message objects from the service to the activity's Handler. The activity -- and only the activity -- can update its widgets, and that only from the main application thread.

Here is a sample application demonstrating this.

Communication between UI thread and other threads using handler

You can pass anything from the parameters if you don't want to use static concepts. In the below codes, I have implemented two classes. As you asked, I used the common handler in both thread classes. I pass handler h1 as the parameters of Runnable object and start() method there to trigger the run() method of another thread class. The thread which contains run() method is the UI (Main) thread. We must use UI thread to update UI. Worker (Background) threads can't do UI update. The communication between worker with UI is done via handler. So, I define handler h2 in UI thread class. When UI thread class constructor is called from background thread class, I get my h2 values from h1 that come from the constructor. And I use h2 for my the communication. In fact, h2 and h1 belong to the same memory space in the system.

I made below two classes and did thread communication for your reference.

First class

 public class MainActivity extends AppCompatActivity {
Handler h1;
Thread t;
EditText editText;
private Bundle bb = new Bundle();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

editText = (EditText) findViewById(R.id.editText);

h1 = new Handler(Looper.getMainLooper()) {

@Override
public void handleMessage(Message msg) {
bb = msg.getData();
String str = bb.getString("udd");
editText.setText(str);
System.out.println(str);
}
};
t = new Thread(new MyRunnable(h1)); //I pass Runnable object in thread so that the code inside the run() method
//of Runnable object gets executed when I start my thread here. But the code executes in new thread
t.start(); //thread started

try {
t.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

}

}

Second class

public class MyRunnable implements Runnable {
private Handler h2;
public MyRunnable(Handler h) {
this.h2 = h;
}

@Override
public void run() {

//everything inside rum method executes in new thread
for(int i=0;i<10;i++) {
Message m = Message.obtain(); //get null message
Bundle b = new Bundle();
b.putString("udd", "daju");
m.setData(b);
//use the handler to send message
h2.sendMessage(m);

}
}
}

Note: when thread.start() happens, it triggers the run of the Runnable class, it creates a separate thread. So every time, you call start(), there is a new thread with the same priority of callee thread.

Hope, this helped you.

Running code in main thread from another thread

NOTE: This answer has gotten so much attention, that I need to update it. Since the original answer was posted, the comment from @dzeikei has gotten almost as much attention as the original answer. So here are 2 possible solutions:

1. If your background thread has a reference to a Context object:

Make sure that your background worker threads have access to a Context object (can be the Application context or the Service context). Then just do this in the background worker thread:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
@Override
public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. If your background thread does not have (or need) a Context object

(suggested by @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
@Override
public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

Making changes to Main Activity UI from thread in Service

All you have to do is to create a Handler on the UI thread:

private Handler serviceHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
someFunctionInTheUIThread();
}
};

And then pass this through to your service. You could have a function in the Service like this:

public void registerHandler(Handler serviceHandler) {
handler = serviceHandler;
}

and then pass the handler through like this:

theService = ((LocalBinder) service).getService();
theService.registerHandler(serviceHandler);

then to send a message back:

Message msg = handler.obtainMessage(IDENTIFIER, "Message or data");
handler.sendMessage(msg);


Related Topics



Leave a reply



Submit