1

I have a class which extends to Thread as follows -

public class ThreadTest extends Thread {

    private Handler handler;
    private Runnable runnable;

    public ThreadTest(Runnable runnable, Handler handler) {

        this.handler = handler;
        this.runnable = runnable;
    }

    @Override
    public void run() {
        super.run();

        Message msg = handler.obtainMessage();
        msg.obj = "YUSSSSSS!";
        handler.sendMessage(msg);

        if (Looper.myLooper() != null) {
            Looper.myLooper().quit();
            Log.i("Looper", "has been quit");
        }

    }
}

Now, I wish to attach a looper to this thread. From my understanding of Looper only the main thread gets a looper attached to it by default.

I try to call Looper.prepare() and Looper.loop() form the constructor of the ThreadTest class like this -

public ThreadTest(Runnable runnable, Handler handler) {

        Looper.prepare();

        this.handler = handler;
        this.runnable = runnable;

        Looper.loop();
    }

But, I get java.lang.RuntimeException: Only one Looper may be created per thread exception at Looper.prepare();.

While, if I attach the looper in Run(), I don't face any problem whatsoever.

What am I doing wrong?

krtkush
  • 1,378
  • 4
  • 23
  • 46

2 Answers2

0

Every thread has a looper and only have one looper, you need to make a check before you are trying to add a looper to a thread.

You need to add if (Looper.myLooper() != null) {} in your constructor too. And remember the looper.myLooper() can only get Main thread Looper if you call it in the thread constructor. Because at that time the new thread has not constructed yet.

brasofilo
  • 25,496
  • 15
  • 91
  • 179
0

When you call Looper.prepare() in the constructor, it's actually being called from whatever other thread that's creating the object by calling its constructor (you didn't specify but I'm guessing the main thread) but the run() method is called from the thread itself. So you should attach your looper in run().

And for run() to be called, you have to call Thread.start() and there's no guaranty that run() will be called immediately after you call start() so if you want the looper to be available immediately after calling the constructor, you can wait in the constructor until run() is called and then notify() the waiting thread in the constructor like this:

public class ThreadTest extends Thread
{
    public ThreadTest()
    {
        //init...

        //wait until thread has started
        waitForRun();
    }

    private void waitForRun()
    {
        try
        {
            synchronized (this)
            {
                start();
                wait();
            }
        }
        catch (InterruptedException ignored)
        {
            
        }
    }

    @Override
    public void run()
    {
        Looper.prepare();
        synchronized (this)
        {
            //notify the waiting thread in the constructor that this thread
            //has started and has a looper
            notifyAll();
        }

        //do stuff...

        Looper.loop();
    }
}
SMMB
  • 107
  • 6