Looper java android ошибка

UPDATE — 2016

The best alternative is to use RxAndroid (specific bindings for RxJava) for the P in MVP to take charge fo data.

Start by returning Observable from your existing method.

private Observable<PojoObject> getObservableItems() {
    return Observable.create(subscriber -> {

        for (PojoObject pojoObject: pojoObjects) {
            subscriber.onNext(pojoObject);
        }
        subscriber.onCompleted();
    });
}

Use this Observable like this —

getObservableItems().
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<PojoObject> () {
    @Override
    public void onCompleted() {
        // Print Toast on completion
    }

    @Override
    public void onError(Throwable e) {}

    @Override
    public void onNext(PojoObject pojoObject) {
        // Show Progress
    }
});
}

———————————————————————————————————————————-

I know I am a little late but here goes.
Android basically works on two thread types namely UI thread and background thread. According to android documentation —

Do not access the Android UI toolkit from outside the UI thread to fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

Activity.runOnUiThread(Runnable)  
View.post(Runnable)  
View.postDelayed(Runnable, long)

Now there are various methods to solve this problem.

I will explain it by code sample:

runOnUiThread

new Thread()
{
    public void run()
    {
        myactivity.this.runOnUiThread(new Runnable()
        {
            public void run()
            {
                //Do your UI operations like dialog opening or Toast here
            }
        });
    }
}.start();

LOOPER

Class used to run a message loop for a thread. Threads by default do
not have a message loop associated with them; to create one, call
prepare() in the thread that is to run the loop, and then loop() to
have it process messages until the loop is stopped.

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

AsyncTask

AsyncTask allows you to perform asynchronous work on your user
interface. It performs the blocking operations in a worker thread and
then publishes the results on the UI thread, without requiring you to
handle threads and/or handlers yourself.

public void onClick(View v) {
    new CustomTask().execute((Void[])null);
}


private class CustomTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... param) {
        //Do some work
        return null;
    }

    protected void onPostExecute(Void param) {
        //Print Toast or open dialog
    }
}

Handler

A Handler allows you to send and process Message and Runnable objects
associated with a thread’s MessageQueue.

Message msg = new Message();


new Thread()
{
    public void run()
    {
        msg.arg1=1;
        handler.sendMessage(msg);
    }
}.start();



Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        if(msg.arg1==1)
        {
            //Print Toast or open dialog        
        }
        return false;
    }
});

Can’t create handler inside thread that has not called looper.prepare() is an error that frequently occurs when you’re working with threads in any Android project.cant create the handler inside thread that has not called looper.prepare

If you can’t understand what exactly is wrong and why you’re seeing this error, don’t worry because we have you covered. Read on as we break down the problem to make it easier to understand and see what you can do to solve it.

Contents

  • Why Do You See Can’t Create Handler Inside Thread That Has Not Called Looper.prepare()?
    • – Calling a UI Component From a Non-UI Worker Thread
  • How To Fix Can’t Create Handler Inside Thread That Has Not Called Looper.prepare()
    • – Run On UI Thread Android
    • – Use Looper
    • – AsyncTask
    • – Handler
  • FAQ
    • 1. What Is a Looper in Android?
    • 2. Which Thread Will Process a Message Posted to a Handler?
    • 3. What Is a UI Thread?
    • 4. What Is the Difference Between Looper and Handler?
  • Conclusion

You’re seeing this error because you’re trying to show or manipulate a UI or user interface component like Toast from a non-UI thread. For instance, you might be calling it from the worker thread when you need to call it from the main thread.

– Calling a UI Component From a Non-UI Worker Thread

There are a few things you should get out of the way first and learn why you’re making Android call looper prepare. Before anything else, you need to re-evaluate why you’re trying to use a handler in a thread that’s not a UI thread, which is basically a system-supplied looper.Calling a UI Component From a Non UI Worker Thread

You should also know that handlers are only because of loopers, and anyone who uses a handler uses it in a UI thread since this thread runs a looper.

Think of a looper like a dispatcher that dispatches different events to the handlers mentioned in your code. The events can be anything ranging from timers expiring to button presses. The basic idea of a UI code is that it should respond to the event and quickly go back to the system.

Another thing you need to understand to fully comprehend the error is the worker thread. This is something you set up to execute on its own without any user interaction. Android provides special mechanisms that allow a worker thread to inform or signal to the UI how it’s doing and when it’s done.

So if you want to use a handler in the worker thread instead of the main or UI thread on which your app runs, you need to be well-versed in loopers and how they’re used. You’re seeing this problem not because you want to create a handler but because you’re trying to call or show a UI component from a non-UI or worker thread.

How To Fix Can’t Create Handler Inside Thread That Has Not Called Looper.prepare()

Android basically has two kinds of threads: the background thread and the UI thread. Android’s official documentation states that to fix this problem, you shouldn’t access the Android UI toolkit from outside the UI thread. Instead, Android provides various ways to solve this create handler issue.

Let’s go over all of them one by one.

– Run On UI Thread Android

The run on UI thread function runs the action specified in the parameters on the UI thread. If the current thread is already the UI thread, the action will be executed immediately. But if the current thread isn’t a UI thread, then the action is sent to the UI thread’s event queue. Here’s a code snippet with this function to help you understand it better:

new Thread() {

public void run() {

myactivity.this.runOnUiThread(new Runnable(){

public void run() {

// Perform some UI operation like a Toast or open a dialog here} });

} }

start();

– Use Looper

Looper is the class used for running the message loop for a thread. By default, threads don’t have any message loop associated with them, and to create one, you first need to call prepare() within the thread that will run the loop.

Then you’ll have to run loop() so that it processes the messages until the loop stops. Let’s go over a Looper.prepare() example to help you understand how and where to use Looper.

class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//process incoming messages}
};
Looper.loop();
} }

And if you’re using Kotlin, the following is a Kotlin handler(looper example). Note that one benefit of using this method is that you can directly run the UI code without any Context or Activity.

Handler(Looper.getMainLooper()).post {

// code goes here}

– AsyncTask

As the name suggests, with AsyncTask, you can do asynchronous work on the user interface. The function essentially performs blocking operations on a worker thread and publishes the results to the UI thread.Cant Create Handler Inside Thread That Has Not Called Looper.prepare Fixes

With this function, you don’t need to handle the handlers or threads yourself. Here’s a code snippet to help you understand the use better:

public void onClick(View v) {
new CustomTask().execute((Void[]null);
}
private class CustomTask extends AsyncTask {
protected Void doInBackground(Void… param) {
// do something
return null;
}
protected void onPostExecute(Void param) {
//open dialog or print Toast}
}

AsyncTask works quite well for nearly all the things that run in the background. You can call its hooks to know its progress and also when it’s done. And while it’s convenient, note that it can leak contexts if you don’t use it correctly. Also, keep in mind that it’s officially deprecated, so it’s best not to use it.

In some cases, you might get an error saying the current thread has not called looper prepare forcing synchronous mode. This means that you shouldn’t use AsyncTask for the task you want to perform; instead, use a synchronous function like SyncHttpClient.

– Handler

With a handler, you can process and message Runnable and Message objects associated with the MessageQueue of a thread. Let’s quickly go over a code snippet to understand its use.

Message msg = new Message();
new Thread() {
public void run() {
msg.arg1=1;
handler.sendMessage(msg); }
}.start();
Handler handler = new Handler(new Handler.Callback() {
@Override public boolean handleMessage(Message msg) {
if(msg.arg1==1){
//Open dialog or print Toast}
return false; }
});

FAQ

1. What Is a Looper in Android?

The Android.os.Looper is a class that’s used to run the message loop for a particular thread. By default, there’s no message loop associated with threads, and you have to call prepare() and then the loop() function so that it can process the messages until the loop stops.

2. Which Thread Will Process a Message Posted to a Handler?

All the input events and UI operations are handled by Android from a single thread called the UI or Main thread. The way this works is that Android collects all the events that take place in this thread and adds them to a queue. This queue is then processed with an instance of the Android Looper class.

3. What Is a UI Thread?

The User Interface or UI thread in Android is basically a Thread element whose purpose is to update the application elements, either explicitly or implicitly. So, if you want to make changes to the attributes or update the elements present in the application layer, i.e., the front end of your application, you’ll have to use the UI thread.

4. What Is the Difference Between Looper and Handler?

Both are different abstractions; looper is an abstraction of the event loop, while the handler inserts or removes event to and from the queue via events and handle them when they’re processed.

Conclusion

Now that you’ve gone through the whole article understanding why you’re seeing the “can’t create handler inside thread that has not called looper.prepare()” error is simple. To sum up, here are the key points we discussed in this article so that you’re fully equipped to solve this issue:

  • Android has 2 kinds of threads: the main thread and the background thread.
  • The error occurs when you try to show UI components from a non-UI or worker thread. You should call it in the main thread.
  • There are many ways to fix this issue. For instance, you can use run on UI thread, Loopers, or Handlers.
  • You can also use AsyncTask, but you should be careful when doing so. This is because it’s deprecated and tends to leak contexts.

Once you go over the different fixes mentioned here, we’re sure that you can quickly solve the issue and get your program to run as it should!

  • Author
  • Recent Posts

Position is Everything

Your Go-To Resource for Learn & Build: CSS,JavaScript,HTML,PHP,C++ and MYSQL. Meet The Team

Position is Everything

UPDATE — 2016

The best alternative is to use RxAndroid (specific bindings for RxJava) for the P in MVP to take charge fo data.

Start by returning Observable from your existing method.

private Observable<PojoObject> getObservableItems() {
    return Observable.create(subscriber -> {

        for (PojoObject pojoObject: pojoObjects) {
            subscriber.onNext(pojoObject);
        }
        subscriber.onCompleted();
    });
}

Use this Observable like this —

getObservableItems().
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<PojoObject> () {
    @Override
    public void onCompleted() {
        // Print Toast on completion
    }

    @Override
    public void onError(Throwable e) {}

    @Override
    public void onNext(PojoObject pojoObject) {
        // Show Progress
    }
});
}

———————————————————————————————————————————-

I know I am a little late but here goes.
Android basically works on two thread types namely UI thread and background thread. According to android documentation —

Do not access the Android UI toolkit from outside the UI thread to fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

Activity.runOnUiThread(Runnable)  
View.post(Runnable)  
View.postDelayed(Runnable, long)

Now there are various methods to solve this problem.

I will explain it by code sample:

runOnUiThread

new Thread()
{
    public void run()
    {
        myactivity.this.runOnUiThread(new Runnable()
        {
            public void run()
            {
                //Do your UI operations like dialog opening or Toast here
            }
        });
    }
}.start();

LOOPER

Class used to run a message loop for a thread. Threads by default do
not have a message loop associated with them; to create one, call
prepare() in the thread that is to run the loop, and then loop() to
have it process messages until the loop is stopped.

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

AsyncTask

AsyncTask allows you to perform asynchronous work on your user
interface. It performs the blocking operations in a worker thread and
then publishes the results on the UI thread, without requiring you to
handle threads and/or handlers yourself.

public void onClick(View v) {
    new CustomTask().execute((Void[])null);
}


private class CustomTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... param) {
        //Do some work
        return null;
    }

    protected void onPostExecute(Void param) {
        //Print Toast or open dialog
    }
}

Handler

A Handler allows you to send and process Message and Runnable objects
associated with a thread’s MessageQueue.

Message msg = new Message();


new Thread()
{
    public void run()
    {
        msg.arg1=1;
        handler.sendMessage(msg);
    }
}.start();



Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        if(msg.arg1==1)
        {
            //Print Toast or open dialog        
        }
        return false;
    }
});

Студворк — интернет-сервис помощи студентам

Соизвольте помочь мне, есть тривиальная задача: создать поток который будет принимать и обробатывать сообщения через handlerб, вот код

Кликните здесь для просмотра всего текста

Мой класс

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestThread extends Thread {
    Handler handler;
 
    @Override
    public void run() {
        super.run();
        Looper.prepare();
        handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                Log.i("лог",Thread.currentThread().getName());
            }
        };
        Looper.loop();
 
    }
}

Кликните здесь для просмотра всего текста

Активити

Java
1
2
3
4
5
6
7
8
9
10
11
12
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TestThread testThread = new TestThread();
        testThread.run();
        testThread.handler.sendEmptyMessage(0);
 
    }
}

при запуске приложение вылетает с ошибкой

Кликните здесь для просмотра всего текста

ЛОГ

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2019-10-09 21:46:57.888 5724-5724/com.example.mytest E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.mytest, PID: 5724
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.mytest/com.example.mytest.MainActivity}: java.lang.RuntimeException: Only one Looper may be created per thread
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
     Caused by: java.lang.RuntimeException: Only one Looper may be created per thread
        at android.os.Looper.prepare(Looper.java:95)
        at android.os.Looper.prepare(Looper.java:90)
        at com.example.mytest.TestThread.run(TestThread.java:19)
        at com.example.mytest.MainActivity.onCreate(MainActivity.java:17)
        at android.app.Activity.performCreate(Activity.java:6975)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)*
        at android.app.ActivityThread.-wrap11(Unknown Source:0)*
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)*
        at android.os.Handler.dispatchMessage(Handler.java:105)*
        at android.os.Looper.loop(Looper.java:164)*
        at android.app.ActivityThread.main(ActivityThread.java:6541)*
        at java.lang.reflect.Method.invoke(Native Method)*
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)*
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)*

если заглянуть в исходники looper и Handler можно увидеть как компилятор ругается на отсутствие библиотек
android.annotation.NonNull
android.annotation.UnsupportedAppUsage
android.util.proto.ProtoOutputStream
наверное это не нормально

#############
если убрать Looper.prepare() из кода, ошибки не будет а код ваполнится в основном потоке

This particular error occurs mainly when working with threads in an Android project.   So straight to the point, no stories.

WHAT IT MEANS

I’m not going to go into all the technicalities of threading, what this error simply means is that you are trying to manipulate or show a User Interface component from a non-UI thread.   A simple example:

Thread thread = new Thread(){

  public void run(){

      Toast.makeText(TimelineActivity.this, «Finco is Daddy», Toast.LENGTH_LONG);

  }  

};

thread.start();

Running the above code will result in the “Can’t create handler inside thread that has not called Looper.prepare()” error.

SOLUTION

  1. Use activity.runOnUiThread():   When manipulating or showing a UI component from a non UI Thread, simply use the runOnUiThread() method.   For example:

    Thread thread = new Thread(){

      public void run(){

          runOnUiThread(new Runnable() {

              public void run() {

                  Toast.makeText(TimelineActivity.this, «Finco is Daddy», Toast.LENGTH_LONG);

              }

          });

      }

    };

    thread.start();

  2. Use Looper:   Here’s another very simple solution:

    Thread thread = new Thread(){

      public void run(){

          Looper.prepare();//Call looper.prepare()

          Handler mHandler = new Handler() {

              public void handleMessage(Message msg) {

                  Toast.makeText(TimelineActivity.this, «Finco is Daddy», Toast.LENGTH_LONG);

              }

          };

          Looper.loop();

      }

    };

    thread.start();

There are many other solutions but I personally find these 2 very easy to implement.

REFERENCE

This StackOverflow question thought me a lot about this Android Error:
https://stackoverflow.com/questions/3875184/cant-create-handler-inside-thread-that-has-not-called-looper-prepare

Feel free to ask any questions or contribute to this post in the comment section below.

Related posts:

Issue

TrendingViewModelTest

@RunWith(JUnit4::class)
class TrendingViewModelTest {
    private lateinit var trendingRepository: TrendingRepository
    private lateinit var trendingViewModel: TrendingViewModel

    @get:Rule
    val schedulers = RxImmediateSchedulerRule()

    @Before
    fun setUp() {
        trendingRepository = mock(TrendingRepository::class.java)
        trendingViewModel = TrendingViewModel(trendingRepository)
    }

    @Test
    fun testWithNetwork() {
        trendingViewModel.isConnected = true
        trendingViewModel.fetchTrendingRepos()
        verify(trendingRepository, times(1)).getTrendingRepos()
    }

    //...
}

TrendingViewModel

fun fetchTrendingRepos() {
if (isConnected) {
    loadingProgress.value = true
    compositeDisposable.add(
        trendingRepository.getTrendingRepos().subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ response ->
                run {
                    loadingProgress.value = false
            },
                { error ->
                    loadingProgress.value = false
                }
            )
    )
} 

RxImmediateSchedulerRule:

class RxImmediateSchedulerRule : TestRule {
    override fun apply(base: Statement?, description: Description?): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
                RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
                RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
                RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }

                try {
                    base?.evaluate()
                } finally {
                    RxJavaPlugins.reset()
                    RxAndroidPlugins.reset()
                }
            }
        }
    }
}

TrendingRepositoryImpl:

class TrendingRepositoryImpl @Inject constructor(
    val apiService: GitHubApi,
    val trendingDao: AppDao
) : TrendingRepository {

    override fun getTrendingRepos(): Single<List<TrendingRepo>> {
        return apiService.getTrendingGits()
    }
}

TrendingRepository:

interface TrendingRepository {
    fun getTrendingRepos(): Single<List<TrendingRepo>>
}

Inside fetchTrendingRepos() an Rxjava call is initiated, it also hooks onto ‘AndroidSchedulers.mainThread()’ that might be the reason causing it.

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.
at android.os.Looper.getMainLooper(Looper.java)
at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:77)
at androidx.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116)
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:461)
at androidx.lifecycle.LiveData.setValue(LiveData.java:304)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.manoj.trendgitz.mvvm.ui.TrendingViewModel.fetchTrendingRepos(TrendingViewModel.kt:32)
at com.manoj.trendgitz.TrendingViewModelTest.testWithNetwork(TrendingViewModelTest.kt:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Solution

As you update LiveData value, you should add @get:Rule var rule: TestRule = InstantTaskExecutorRule() also.

Don’t forget to add following to build.gradle file:

dependencies {
    // ...
    testImplementation "androidx.arch.core:core-testing:2.1.0"
}

Also, change your test code accordingly to avoid NullPointerException:

@Test
fun testWithNetwork() {
    trendingViewModel.isConnected = true
    Mockito.`when`(trendingRepository.fetchTrendingRepos()).thenReturn(Single.just(listOf<TrendingRepo>()))
    trendingViewModel.fetchTrendingRepos()
    verify(trendingRepository, times(1)).getTrendingRepos()
}

Mockito.when() lets you to do different actions every time your mock method is called. If you don’t use it, you may see possible NullPointerException depending on your test function.

Answered By — Natig Babayev
Answer Checked By — Senaida (JavaFixing Volunteer)

Have you ever tried to launch Android Toast message from worker thread ? Probably you are wondering why the heck it is giving this error-

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

In this article we are going to explore reason behind the above exception and try to understand how Looper works in Android. At the end, I am going to explain one approach to run Toast from a worker thread, but before that we need to understand how Looper works. If you already know in/out of Looper, you can skip below section and directly move to the solution part.

Looper is a very basic wrapper class which attach a MessageQueue to a Thread and manage this queue. MessageQueue is a structure to sequentialize simultaneous processing requests of a Thread.  In Android, message/request processing classes like Handler uses Looper to manage their respective MessageQueue.

Looper = Thread + MessageQueue

Android Looper Life Cycle:

As you can see in the above figure, Looper Life cycle starts when we call prepare(), this static method creates instance of Looper class and store this in a ThreadLocal variable. Below code snippet from Looper.java

    private static void prepare(boolean quitAllowed) {

        if (sThreadLocal.get() != null) {

            throw new RuntimeException(«Only one Looper may
be created per thread»
);

        }

        sThreadLocal.set(new Looper(quitAllowed));

   
}

    private Looper(boolean quitAllowed) {

        mQueue = new MessageQueue(quitAllowed);

        mRun = true;

        mThread = Thread.currentThread();

   
}

Once Looper instance is created we have to call loop() which starts an infinite Loop and process requests accumulated in the Message Queue. Below code from Looper.java

    public static void loop() {

    // Looper instance and Thread
verification

        final MessageQueue queue = me.mQueue;

        for (;;) {

            Message
msg = queue.next();
// might block

            if (msg == null) {

                // No message indicates that the message queue is quitting.

                return;

            }

            // Process Messages

        }

   
}

Due to above Infinite Loop, Looper blocks the current Thread execution until we call Quit. To get a hook to know when Looper is done with Message processing or when Message Queue is empty, we need to register MessageQueue.IdleHandler listener to the MessageQueue associated to the respective Looper. This can be achieved as- 

// Prepare looper

Looper.prepare();

// Register Queue listener hook

MessageQueue queue = Looper.myQueue();

queue.addIdleHandler(new IdleHandler() {

@Override

     public boolean queueIdle() {

          // TODO Auto-generated method stub

          return false;

     }

});

// Start looping Message Queue

Looper.loop();

Running Toast from Worker Thread

Now, lets explain how we can run Toast in a thread other than UI or Main Thread. If we look inside of Toast.java, when we call makeText() internally it initialize Handler on the Calling thread. So, if the calling thread has not yet been attached to a MessageQueue Toast fails to create its instance. The only way to attach MessageQueue to a Thread is to use Looper, as a result Toast.java throws below exception-

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

Toast.java -> Toast.TN.java -> Handler.java

public Toast(Context context) {

        mTN = new TN();

        mTN.mY = context.getResources().getDimensionPixelSize(

                com.android.internal.R.dimen.toast_y_offset);

}

public Handler() {

        // …….

        mLooper = Looper.myLooper();

        if (mLooper == null) {

            throw new RuntimeException(

                «Can’t create handler inside thread that has not called
Looper.prepare()»
);

        }

        mQueue = mLooper.mQueue;

        mCallback = null;

}


So, I hope we now understand the real meaning of this famous error message. Lets now try an alternate approach to execute Toast in a separate Thread.

We need to initialize Looper and attach MessageQueue to our calling Thread prior to launch Toast. When we call show() method of Toast, it sends Handler message to process display logic of Toast, then it waits for defined time (LONG/SHORT) and then calls hide() which again sends Handler message to remove Toast View. Thus Looper associated to Toast is requesting for MessageQueue twice, so we’ll keep a counter to track number of MessageQueue requests and when it is 2, we’ll quit the Looper. It is really important to quit the Looper, as if we don’t do so, it will block all further operations, since Looper.loop() is a blocking call.

Here is the code snippet to run Toast in a separate Thread-

     public void aboutLooper() {

         Thread th = new Thread() {

              public void run() {

                  System.out.println(«Start Looper…»);

                  // Prepare looper

                  Looper.prepare();

                  // Register Queue listener hook

                  MessageQueue
queue = Looper.myQueue();

                  queue.addIdleHandler(new IdleHandler() {

                       int mReqCount = 0;

                       @Override

                       public boolean queueIdle() {

                           if (++mReqCount == 2) {

                                // Quit looper

                                Looper.myLooper().quit();

                                return false;

                           } else

                                return true;

                       }

                  });

                  // Show Toast- will be called when Looper.loop() starts

                  Toast.makeText(MainActivity.this, «Hey
there!!»
,

                           Toast.LENGTH_LONG).show();

                  // Start looping Message Queue- Blocking call

                  Looper.loop();

                  System.out.println(«It appears after Looper.myLooper().quit()»);

              }

         };

         th.start();

     }

here is my code in background service

public class BackgroundService extends Service {
    private int interval1 = 60; // 60 seconds
    private int interval2 = 60; // 60 seconds
    //=============================================================================================================================
    private Handler mTimer1 = new Handler();
    private Runnable mTask1 = new Runnable() {
        public void run() {
            Looper.prepare();
            Log.w("GPS Tracker", "Tracker going to run "+new Date());
            LocationManager mlocManager =(LocationManager)getSystemService(Context.LOCATION_SERVICE);
            LocationListener mlocListener = new MyLocationListener();
            mlocManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, mlocListener);//, Looper.getMainLooper());
            mTimer1.postDelayed(this, interval1 * 1000L);
            Looper.loop();
        }
    };

//=============================================================================================================================
    private Handler mTimer2 = new Handler();
    private Runnable mTask2 = new Runnable() {
        public void run() {
            if (isOnline() == true) {
                Log.w("Method 2", "setUpdateCardAcceptTag");
                setUpdateCardAcceptTag();
                Log.w("Method 3", "getCardBulkSerialData");
                getCardBulkSerialData();
                Log.w("Method 12", "updateStockDataFromRemote");
                updateStockDataFromRemote();
                Log.w("Method 5", "SetRemarksData");
                SetRemarksData();
                Log.w("Method 6", "SetCardSaleData");
                SetCardSaleData();
                Log.w("Method 7", "synchMerchants");
                synchMerchants();
                Log.w("Method 9", "getUpdatedCities");
                getUpdatedCities();
                Log.w("Method 10", "getNotifications");
                getNotifications();
                Log.w("Method 11", "getNextSerialDetails");
                getNextSerialDetails();
                Log.w("Method 12", "getNextSerialDetails");
                //synchLocations();
                Log.w("Method 13", "synchLocations");

            }
            mTimer2.postDelayed(this, interval2 * 1000L);
        }
    };

when i run the application its stopped after few seconds.its says below error message in log console please help me to sort out this issue

thanks


You can not prepare the Looper multiple times for a Thread. before preparing, Chcek whether Looper is associated with that Thread or not.

private Runnable mTask1 = new Runnable() {
    public void run() {
        if(Looper.myLooper() == null) { // check already Looper is associated or not.
           Looper.prepare(); // No Looper is defined So define a new one
        }
        Log.w("GPS Tracker", "Tracker going to run "+new Date());
        LocationManager mlocManager =(LocationManager)getSystemService(Context.LOCATION_SERVICE);
        LocationListener mlocListener = new MyLocationListener();
        mlocManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, mlocListener);//, Looper.getMainLooper());
        mTimer1.postDelayed(this, interval1 * 1000L);
        Looper.loop();
    }
};


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

Hello,

I am trying to run some code not in the main activity involving controls on Main.

I tried this from secondary class:

I tried several ways obtaing always this error: Can’t create handler inside thread that has not called Looper.prepare()

Would you help me ??


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

Try to call it from within the Main Activity, does it work?

code is emotional

Giovanni Montano

Ranch Hand

Posts: 600

Android
Python
Open BSD
VI Editor
Slackware


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

or more easy try AsyncTask!

code is emotional

simone giusti

Ranch Hand

Posts: 55


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

Giovanni Montano wrote:Try to call it from within the Main Activity, does it work?

The activity that has to update Main starts on a secondary class

simone giusti

Ranch Hand

Posts: 55


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

Giovanni Montano wrote:or more easy try AsyncTask!

I tried …

now I have the same looper error plus a new one !

This is the Async code:

Giovanni Montano

Ranch Hand

Posts: 600

Android
Python
Open BSD
VI Editor
Slackware


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

try with thisthis

Also not sure you updated AsyncTask in the right way you should update the UI thread or main I guess if is there that you set the layout in the method onPostExecute, not in onBackground

code is emotional

Giovanni Montano

Ranch Hand

Posts: 600

Android
Python
Open BSD
VI Editor
Slackware


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

I signal you also this link on how to communicate with UI thread. Let me know Simone!

code is emotional

simone giusti

Ranch Hand

Posts: 55


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

Giovanni Montano wrote:I signal you also this link on how to communicate with UI thread. Let me know Simone!

Grazie Giovanni,

I tried async task  and runonUi but I obtain always a looper error.

All I want to do is refreshing a listview!

Giovanni Montano

Ranch Hand

Posts: 600

Android
Python
Open BSD
VI Editor
Slackware


posted 6 years ago


  • Likes 1
  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

simone giusti wrote:

Giovanni Montano wrote:I signal you also this link on how to communicate with UI thread. Let me know Simone!

Grazie Giovanni,

I tried async task  and runonUi but I obtain always a looper error.

All I want to do is refreshing a listview!

For that the best way to go is to use a CursorLoader, because I imagine you are going to query a DB.

Anyway is an easy operation, I am afraid your AsyncTask is not correct. you need to update the UI -Main Thread in onPostExecute.

And in any case if you do Android to work in a company, today the way to go is RXJava 2

code is emotional

simone giusti

Ranch Hand

Posts: 55


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

Giovanni Montano wrote:

simone giusti wrote:

Giovanni Montano wrote:I signal you also this link on how to communicate with UI thread. Let me know Simone!

Grazie Giovanni,

I tried async task  and runonUi but I obtain always a looper error.

All I want to do is refreshing a listview!

For that the best way to go is to use a CursorLoader, because I imagine you are going to query a DB.

Anyway is an easy operation, I am afraid your AsyncTask is not correct. you need to update the UI -Main Thread in onPostExecute.

And in any case if you do Android to work in a company, today the way to go is RXJava 2

Thanks ! it works using onPostExecute …

now I will study how CursorLoader operates …

Giovanni Montano

Ranch Hand

Posts: 600

Android
Python
Open BSD
VI Editor
Slackware


posted 6 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:

  • Quote
  • Report post to moderator

simone giusti wrote:

Giovanni Montano wrote:

simone giusti wrote:

Giovanni Montano wrote:I signal you also this link on how to communicate with UI thread. Let me know Simone!

Grazie Giovanni,

I tried async task  and runonUi but I obtain always a looper error.

All I want to do is refreshing a listview!

For that the best way to go is to use a CursorLoader, because I imagine you are going to query a DB.

Anyway is an easy operation, I am afraid your AsyncTask is not correct. you need to update the UI -Main Thread in onPostExecute.

And in any case if you do Android to work in a company, today the way to go is RXJava 2

Thanks ! it works using onPostExecute …

now I will study how CursorLoader operates …

Glad to help, please consider that CursorLoader and RXJava 2 are the way companies are expecting you will go in 2017

code is emotional

i’m getting this error which i can’t understand why. I’m fairly new to java. What i’m trying to do is start a new thread which will handle all the save image processes.

Error:

 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                at android.os.Handler.<init>(Handler.java:200)
                at android.os.Handler.<init>(Handler.java:114)
                at android.app.Dialog.<init>(Dialog.java:108)
                at android.app.AlertDialog.<init>(AlertDialog.java:125)
                at android.app.AlertDialog$Builder.create(AlertDialog.java:967)
                at android.app.AlertDialog$Builder.show(AlertDialog.java:986)
                at com.example.matts.myapplication.MainActivity$1.handleMessage(MainActivity.java:37)
                at com.example.matts.myapplication.Saving.run(Saving.java:54)
                at java.lang.Thread.run(Thread.java:818)

Here’s my code.

Main:

package com.example..myapplication;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.text.SimpleDateFormat;


public class MainActivity extends Activity {

    static final int REQUEST_IMAGE_CAPTURE = 1;
    ImageView imageView;
    private static final String TAG = "MyActivity";
    Bitmap image;

    Handler goodHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
            builder.setMessage("File Saved").show();

        }

    };

    Handler badHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
            builder.setMessage("File Save failed").show();

        }

    };



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        openCamera();
        setContentView(R.layout.activity_main);
        findViewById(R.id.captureImage).bringToFront();
        findViewById(R.id.saveImage).bringToFront();
        imageView = (ImageView) findViewById(R.id.imageView2);

    }

    protected void onSaveInstanceState(Bundle outState){
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
        file.delete();
    }

    public void openCamera() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }


    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //Check that request code matches ours:
        if (requestCode == REQUEST_IMAGE_CAPTURE) {
            //Get our saved file into a bitmap object:
            File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
            Bitmap image = decodeSampledBitmapFromFile(file.getAbsolutePath(), 1000, 700);
            imageView.setImageBitmap(image);
        }
    }

// Reduce the amount of dynamic heap used by expanding the JPEG into a memory array that's already scaled to match the size of the destination view
    public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) { // BEST QUALITY MATCH
        //First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Calculate inSampleSize, Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        int inSampleSize = 1;

        if (height > reqHeight) {
            inSampleSize = Math.round((float) height / (float) reqHeight);
        }
        int expectedWidth = width / inSampleSize;

        if (expectedWidth > reqWidth) {
            //if(Math.round((float)width / (float)reqWidth) > inSampleSize) // If bigger SampSize..
            inSampleSize = Math.round((float) width / (float) reqWidth);
        }

        options.inSampleSize = inSampleSize;

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(path, options);
    }

    public void capture_btn(View v) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }



    public void storeImage(Bitmap image) {
        imageView.buildDrawingCache();
        Bitmap bm_img = imageView.getDrawingCache();
        Saving saving = new Saving(bm_img, goodHandler, badHandler);
        Thread thread = new Thread(saving);
        thread.start();
    }

    public void save_btn(View v) {
        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which){
                    case DialogInterface.BUTTON_POSITIVE:
                        storeImage(image);


                        break;

                    case DialogInterface.BUTTON_NEGATIVE:

                        break;
                }
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("Are you sure?").setPositiveButton("Yes", dialogClickListener)
                .setNegativeButton("No", dialogClickListener).show();

    } //notification messages
}

Saving:

package com.example..myapplication;

import android.graphics.Bitmap;
import android.os.Environment;
import android.os.Message;
import android.util.Log;
import android.os.Handler;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;


public class Saving implements Runnable {
    private Bitmap image;
    private android.os.Handler goodHandler;
    private android.os.Handler badHandler;
    private static final String TAG = "Saving";

    public Saving(Bitmap in, Handler hinGood,Handler hinBad) {
        image = in;
        goodHandler = hinGood;
        badHandler = hinBad;


    }

    @Override
    public void run() {
        File file = getOutputMediaFile();
        if (file == null) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(file);
            image.compress(Bitmap.CompressFormat.PNG, 90, fos);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
            badHandler.handleMessage(new Message());
            return;
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
            badHandler.handleMessage(new Message());
            return;
        }
        goodHandler.handleMessage(new Message());


    }

    public File getOutputMediaFile(){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.
        File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
                + "/Pictures/Wiki_camera"
                + "/Photos");

        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                return null;
            }
        }
        // Create a media file name
        String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
        File mediaFile;
        String mImageName="camera_wiki"+ timeStamp +".jpg";
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
        return mediaFile;
    }

}

I’ve added whole of the main class and the saving class.

Понравилась статья? Поделить с друзьями:
  • Lost alpha configuration произошла непредвиденная ошибка
  • Lookupvalue dax ошибка
  • Lorch micormig 500 ошибки
  • Look it rains ошибка
  • Lords mobile ошибка 12002