Search code examples
androidandroid-handlerandroid-workmanager

Can't create handler inside thread that has not called Looper.prepare() in WorkManager


I am trying to implement a WorkManager for a periodic background operation. I'm getting a common error when trying to use a Handler in the Worker. I know the error is saying I need to call Looper.prepare() because I'm on the wrong thread but I'm not sure how to.

This is the code I'm using:

public class SyncWorker extends Worker {

    private static TimeOutHandler mTimeOutHandler;

    public SyncWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
        mTimeOutHandler = new TimeOutHandler(this);
    }

    @Override
    public Result doWork() {    
        return Result.success();
    }

    private static class TimeOutHandler extends Handler {
        private final WeakReference<SyncWorker> mMainWeakReference;

        //this is where the error is thrown
        TimeOutHandler(final SyncWorker service) {
            mMainWeakReference = new WeakReference<>(service);
        }

        @Override
        public void handleMessage(final Message msg) {
            final SyncWorker service = mMainWeakReference.get();

            if (service != null) {
                switch (msg.what) {
                    case MESSAGE_CONNECTIVITY_TIMEOUT:
                        mTimeOutHandler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT);
                        break;
                }
            }
        }
    }
}

Handler handler = new Handler(Looper.getMainLooper());

I've done some research and one suggested solution is to use:

Handler handler = new Handler(Looper.getMainLooper());

but I don't know how to do that with the way I'm extending Handler.


Solution

  • When You've created constructor extending Handler class, and you've added parameters for your constructor there are several ways to provide Looper to your Handler like below suggested approach.

    1. You can create another constructor that access Looper also:

      TimeOutHandler(final SyncWorker service, Looper looper) {
          super(looper);
          mMainWeakReference = new WeakReference<>(service);
      }
      

      And initialize like :

      TimeOutHandler handler = new TimeOutHandler(service, Looper.getMainLooper());
      
    2. Directly passing Looper to primary constructor:

      TimeOutHandler(final SyncWorker service) {
          super(Looper.getMainLooper());
          mMainWeakReference = new WeakReference<>(service);
      }
      

    So calling super() initialize constructor of parent (super) class & in our case, we pass Looper as parameter to super will initialize Handler with constructor Handler(Looper looper) which previously was Handler() (Empty or default constructor).

    Note: The thing is Handler class has multiple constructors from which one accepts Looper which is provided to switch Handler to specific thread provided.