Reputation: 2418
I have an Observer Design pattern that uses a Thread to send my own Events
. Sometimes the classes that are called are Android Activities and other times they are regular classes. The code in the Thread is NOT an Activity.
I was getting this error:
Can't create handler inside thread that has not called Looper.prepare()
To avoid that run-time error I had to add Looper.prepare()
inside the Thread. I am aware of this post: Can't create handler inside thread that has not called Looper.prepare() it contains the best information for this kind of problem, but still I don't know how to integrate the answer by mjosh into my Thread.
The call from the Thread to the Activity never happens. In isolation this code works, but not when Activities are waiting for the call.
This class will send Events to other classes. This one runs with a Thread.
public class EventBus
{
// Non essential code here.
...
// Thread code.
private class MyThread implements Runnable
{
@Override
public void run()
{
while(true)
{
synchronized(list)
{
for(Observer o : list)
// Execution transfers to Activity here.
o.handleEvent(event);
}
}
}
}
}
Inside an Activity.
public class FirstActivity extends Activity implements Observer
{
public void handleEvent()
{
// Never gets here.
}
}
So how do I solve this problem? My whole app will have many Activities and many regular classes.
I need a way to keep my Thread alive and making calls to Activities. This might be a mixture of adding some code to the Thread so that it can successfully send my calls to the Activities.
Upvotes: 4
Views: 715
Reputation: 2803
While I dont program for Android, I figure it cant be all that difficult to do what you want here. From the answer given in your linked question, the issue is that you will have observers which may or may not be an Activity. You cant simply handle the event on the activity because of the nature of the Activity threading model. For Activities you need to have the action done on the UI thread. This sounds pretty strait forward. Using POJO's I've put together a SSCCE below. The only part that would really be of much interest is the casting that takes place in the EventBus's loop.
public class Test {
public static void main(String[] args) throws InterruptedException {
//create the thread to demonstrate execution
MyThread t = new MyThread();
//add a non activity observer
t.addObserver(new NonActivityObserver());
//add an activity observer
t.addObserver(new ActivityDecendent());
//fire off the thread
new Thread(t).start();
//give us time to read the output
Thread.sleep(Long.MAX_VALUE);
}
public static class MyThread implements Runnable
{
private ArrayList<Observer> list = new ArrayList<Observer>();
public void addObserver(Observer arg0) {
synchronized (list) {
list.add(arg0);
}
}
@Override
public void run()
{
//no loop just doing this once for example
synchronized(list)
{
for(final Observer o : list) {
//first try to cast to an Activity
try {
Activity act = (Activity)o;
//if we get here, its an activity
//so invoke on its UiThread
act.runOnUiThread(new Runnable() {
@Override
public void run() {
Event event = new Event("From the activity's runOnUiThread method");
o.handleEvent(event);
}
});
}
catch (Exception e) {
//if we got here, the class is not an activity
//so it should be safe to just handle the event
//on this thread
Event event = new Event("From observers handle event method");
o.handleEvent(event);
}
}
}
}
}
//your observer interface
public static interface Observer {
public void handleEvent(Event e);
}
//Your Event class
public static class Event {
private String message;
public Event(String message) {
this.message = message;
}
public void talk() {
System.out.println(message);
}
}
//An observer which isnt an activity
public static class NonActivityObserver implements Observer {
@Override
public void handleEvent(Event e) {
e.talk();
}
}
//An activity which implements Observer
public static class ActivityDecendent extends Activity implements Observer {
@Override
public void handleEvent(Event e) {
e.talk();
}
}
//An Activity
public static class Activity {
//pretend this is the Android activity executing this runnable
//on the UI thread
public void runOnUiThread(Runnable r) {
new Thread(r).start();
}
}
}
And the output:
From observers handle event method
From the activity's runOnUiThread method
This solution is sorta specific to your case, which sucks... but I think this should work.
Upvotes: 3