Search code examples
javaandroidclipboardandroid-workmanagerclipboardmanager

ClipboardManager throws the following error: Can't create handler inside thread that has not called Looper.prepare() using WorkManager


I'm creating an app where the user has the possibility to copy the text to clipboard for 30 seconds. After 30 seconds the text needs to be removed from clipboard, even if the app has been closed

In my main activity I have the following code:

OneTimeWorkRequest worker = new OneTimeWorkRequest.Builder(clipboardWorker.class)
                .setInitialDelay(30, TimeUnit.SECONDS).build();
        WorkManager.getInstance().enqueue(worker);

The clipboardWorker looks like this:

public class clipboardWorker extends Worker {
    private static final String TAG = "clipboardWorker";

    public clipboardWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {

        ClipboardManager clipboardManager = (ClipboardManager) getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("clp", "");
        clipboardManager.setPrimaryClip(clip);

        return Result.success();
    }
}

This works on Android version 28 but on 27 it throws the following error:

Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

Here is the stack trace:

2019-04-07 11:41:15.948 18924-18944/com.example.clipboard E/WM-WorkerWrapper: Work [ id=fa06342b-0130-4562-b323-5871ea0e67fb, tags={ com.example.clipboard.clipboardWorker } ] failed because it threw an exception/error
    java.util.concurrent.ExecutionException: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:516)
        at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
        at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:289)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:204)
        at android.os.Handler.<init>(Handler.java:118)
        at android.content.ClipboardManager$2.<init>(ClipboardManager.java:62)
        at android.content.ClipboardManager.<init>(ClipboardManager.java:62)
        at android.app.SystemServiceRegistry$11.createService(SystemServiceRegistry.java:254)
        at android.app.SystemServiceRegistry$11.createService(SystemServiceRegistry.java:252)
        at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:962)
        at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:914)
        at android.app.ContextImpl.getSystemService(ContextImpl.java:1667)
        at android.content.ContextWrapper.getSystemService(ContextWrapper.java:714)
        at com.example.clipboard.clipboardWorker.doWork(clipboardWorker.java:26)
        at androidx.work.Worker$1.run(Worker.java:85)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at java.lang.Thread.run(Thread.java:764) 

The error is thrown here:

ClipboardManager clipboardManager = (ClipboardManager) getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);

Below are the gradle plugins:

    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.work:work-runtime:2.0.0'

Solution

  • That's because The Worker Class of the WorkManager Api run on a Background Thread,

    In the Worker Class you could try to declare an Handler attached to the Main Thread :

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

    Then use the Handler's post() Method to run your ClipBoard Code