Я создаю приложение, в котором у пользователя есть возможность скопировать текст в буфер обмена на 30 секунд. Через 30 секунд текст необходимо удалить из буфера обмена, даже если приложение было закрыто.
В моей основной деятельности у меня есть следующий код:
OneTimeWorkRequest worker = new OneTimeWorkRequest.Builder(clipboardWorker.class)
.setInitialDelay(30, TimeUnit.SECONDS).build();
WorkManager.getInstance().enqueue(worker);
ClipboardWorker выглядит следующим образом:
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();
}
}
Это работает на Android версии 28, но на 27 выдает следующую ошибку:
Вызвано: java.lang.RuntimeException: невозможно создать обработчик внутри потока, который не вызвал Looper.prepare()
Вот трассировка стека:
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)
Ошибка вылетает здесь:
ClipboardManager clipboardManager = (ClipboardManager) getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
Ниже приведены плагины Gradle:
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'
Я имею в виду, вот так: drive.google.com/file/d/1Rh6nH4AzyEI_2f1rhmauwHBKWbrog2c2/…. Извините, я пытался сокращать вещи в своем комментарии, и это только запутало.
Привет Майк, спасибо за ответ. Я изменил код точно так же, как и вы, но кажется, что ошибка сохраняется.
Хорошо, я не был уверен, что Worker
были созданы в основном потоке. Судя по всему, это не так. Проверьте эту ссылку еще раз и попробуйте.
Это работает, спасибо.
Это связано с тем, что рабочий класс API WorkManager запускается в фоновом потоке,
В рабочем классе вы можете попытаться объявить обработчик, прикрепленный к основному потоку:
Handler mainThreadHandler = new Handler(Looper.getMainLooper());
Затем используйте метод post() обработчика для запуска кода буфера обмена.
Хм, да, ты прав. До уровня API 28
ClipboardManager
создает экземплярHandler
в инициализаторе поля, поэтому возникает ошибка, когда вы пытаетесь получить эту службу вdoWork()
. С 28 года ему передаетсяHandler
через его конструктор. Попробуйте сделатьclipboardManager
поле — например,private ClipboardManager clipboardManager;
— и попасть в конструктор вашегоclipboardWorker
после вызоваsuper
—clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
.