• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

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

mcpixel

Newbie
Stack trace:
Java:
08-01 19:19:43.254 5291-5291/com.example.app1 E/APP: RUNNING
08-01 19:19:43.254 5291-5291/com.example.app1 E/APP: onChanged
08-01 19:19:43.259 5291-5320/com.example.app1 E/WM-WorkerWrapper: Work [ id=495d1e86-0f1f-42a5-9997-3a3c01b878d3, tags={ *****, com.example.app1.UploadWorker } ] 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:284)
        at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:75)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
        at java.lang.Thread.run(Thread.java:856)
     Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:121)
        at android.app.Activity.<init>(Activity.java:749)
        at androidx.core.app.ComponentActivity.<init>(ComponentActivity.java:39)
        at androidx.activity.ComponentActivity.<init>(ComponentActivity.java:84)
        at androidx.fragment.app.FragmentActivity.<init>(FragmentActivity.java:127)
        at androidx.appcompat.app.AppCompatActivity.<init>(AppCompatActivity.java:77)
        at com.example.app1.MainActivity.<init>(MainActivity.java:19)
        at com.example.app1.UploadWorker.doWork(UploadWorker.java:20)
        at androidx.work.Worker$1.run(Worker.java:85)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
        at java.lang.Thread.run(Thread.java:856)
UploadWorker.java:
Java:
package com.example.app1;

import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class UploadWorker extends Worker {

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

    @NonNull
    @Override
    public Result doWork() {
        String key = new MainActivity().KEY_NAME;
        Data ret = new Data.Builder().putLong(key, System.currentTimeMillis()).build();
        Log.e("APP", "doWork");
        return Result.success(ret);
    }
}
MainActivity.java:
Java:
package com.example.app1;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;

import android.os.Bundle;
import android.util.Log;

import java.util.List;
import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

    public final String KEY_NAME = "string1";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.e("START", "Start");

        Constraints constraints = new Constraints.Builder().setRequiresCharging(true).build();
        Data data = new Data.Builder().putString(KEY_NAME, "Mike").build();
        OneTimeWorkRequest uploadWordRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
                .setConstraints(constraints)
                .setInitialDelay(10, TimeUnit.SECONDS)
                .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.SECONDS)
                .setInputData(data)
                .addTag("tag")
                .build();
        WorkManager w = WorkManager.getInstance(getApplicationContext());
        w.enqueue(uploadWordRequest);
        LiveData<List<WorkInfo>> l = w.getWorkInfosByTagLiveData("tag");
        l.observe(this, new Observer<List<WorkInfo>>() {
            @Override
            public void onChanged(List<WorkInfo> workInfos) {
                if(workInfos.get(0).getState() == WorkInfo.State.RUNNING)
                    Log.e("APP", "RUNNING");
                Log.e("APP", "onChanged");
            }
        });
    }
}
I am writing on android 4.1.1 and it seems this error applies to my code, but I did not understand how to fix it. I will be glad to any help
 
The first thing you should do is re-evaluate why you are trying to use a handler in a thread other than the UI thread (which as a system-supplied looper).

Handlers only work by virtue of loopers. Almost everybody who uses handlers uses them in the UI thread because the UI thread runs under a looper. A looper is like a dispatcher that dispatches events to handlers in your code. These events can be anything from button presses to timers expiring. The UI code is built upon the idea that it is supposed to respond to events and then quickly return to the system (which is a looper).

A worker thread, on the other hand, is generally something that you start up to do something on its own without any interaction with the user. There are special mechanisms provided by Android to allow a worker thread to signal to the UI how it is going and when it is done. But basically the thread just runs straight line code, being interrupted only by Android OS time sharing and by calling blocking system methods (functions that must wait for something to happen before they can return.) It is never a good idea to have a thread sit "spinning its wheels" so to speak.

So if you really want to use a handler in your worker thread (as distinct from the User Interface thread on which the rest of your app runs) you have to become very familiar with loopers and how they are used. But I suspect you don't really want to create a handler. If you want to communicate with the UI thread, then use one of the recommended methods of doing that.
 
The first thing you should do is re-evaluate why you are trying to use a handler in a thread other than the UI thread (which as a system-supplied looper).

Handlers only work by virtue of loopers. Almost everybody who uses handlers uses them in the UI thread because the UI thread runs under a looper. A looper is like a dispatcher that dispatches events to handlers in your code. These events can be anything from button presses to timers expiring. The UI code is built upon the idea that it is supposed to respond to events and then quickly return to the system (which is a looper).

A worker thread, on the other hand, is generally something that you start up to do something on its own without any interaction with the user. There are special mechanisms provided by Android to allow a worker thread to signal to the UI how it is going and when it is done. But basically the thread just runs straight line code, being interrupted only by Android OS time sharing and by calling blocking system methods (functions that must wait for something to happen before they can return.) It is never a good idea to have a thread sit "spinning its wheels" so to speak.

So if you really want to use a handler in your worker thread (as distinct from the User Interface thread on which the rest of your app runs) you have to become very familiar with loopers and how they are used. But I suspect you don't really want to create a handler. If you want to communicate with the UI thread, then use one of the recommended methods of doing that.
I figured out this question by removing from the code the creation of a new MainActivity class to access the variable inside it. That was my biggest mistake.
 
Back
Top Bottom