4

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.


Problem

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.

To Solve my Problem

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.

Community
  • 1
  • 1
J_Strauton
  • 2,270
  • 3
  • 28
  • 70
  • 2
    post logcat error, as we don't know what error you get, we can't help you – Shayan Pourvatan Sep 20 '14 at 05:40
  • your description of your problem is very bad, and you used my answer and put it in your question edited that and instead of saying thanks at least complain it dose not solve it so every one comes and votes it down, i solved your first error of `Can't create handler inside thread that has not called Looper.prepare()`. and tell why it causes. i give you a full description of why it happens first then you used it and searched for another similar answer on SO and say `i am aware of`. – mmlooloo Sep 21 '14 at 02:12
  • @mmlooloo I disagree, my question is not as bad as you claim. You don't seem to understand it because you have shown you don't know what the Observer pattern is. `Looper.prepare()` was not a big revelation, believe it or not, the "solution" for that is in the error log. Most importantly you claim that I can communicate with the UI, and yet if you test my code or read above you will see there is **no communication from the Thread to the Activity, that is the main issue**. – J_Strauton Sep 21 '14 at 02:27
  • @mmlooloo Sadly, you didn’t answer the problem and you even stated that we should wait for someone to answer it, yet you didn’t remove your answer because it had 1 upvote. Your “answer” was just copy pasted from other answers, you didn’t even try to compile it. I had to point out that it wouldn’t compile. **SO is a fair place, if your answer was that good then undelete it and let people decide if it is the right answer or not.** – J_Strauton Sep 21 '14 at 02:29
  • because you always editing your question but i have not, you continuing to edit and change the question and if i copy and paste any thing i always add a reference to it and because it is accepted i thought it works you told me it misses `;` – mmlooloo Sep 21 '14 at 02:30
  • @mmlooloo It was more than a `;`... I improved my post but it is still the same question. Your answer, however, started as a link only answer. Only after someone downvoted you did you edit it. This conversation is over and highly unproductive. Again SO is a fair place, let others decide, I will wait for a good answer. One that actually lets my Thread communicate with my Activity -- that has always been the problem. – J_Strauton Sep 21 '14 at 02:32
  • i edited it after you added your `logcat` at first you did not add that, you are continuing to change it. – mmlooloo Sep 21 '14 at 02:34
  • Have you thought about using an existing framework? Such as eventbus: https://github.com/greenrobot/EventBus. Or you can look at the source to see how they post messages to the main(ui) thread. – Brian Sep 22 '14 at 04:32

1 Answers1

3

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.

Mark W
  • 2,791
  • 1
  • 21
  • 44