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;
    }
});

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:

In this guide, we’ll walk you through the steps to fix the ‘Can’t Toast on a Thread That Has Not Called Looper.prepare()’ error in Android. This error is often encountered when trying to display a Toast message from a non-UI thread. We’ll cover the reasons behind this error and provide you with step-by-step solutions to resolve it.

Table of Contents

  • Understanding the Error
  • Solution 1: Using runOnUiThread
  • Solution 2: Using Handler
  • FAQ
  • Related Links

Understanding the Error

The ‘Can’t Toast on a Thread That Has Not Called Looper.prepare()’ error occurs when you try to display a Toast message from a non-UI thread. In Android, UI-related tasks must be executed on the main (UI) thread. However, when you attempt to display a Toast message from a background thread, it raises this error.

Solution 1: Using runOnUiThread

One way to resolve this error is by using the runOnUiThread method. This method allows you to execute UI-related tasks on the main thread from a background thread. Here’s how to use runOnUiThread to display a Toast message:

public void showToastFromBackgroundThread() {
    final String toastMessage = "Hello, World!";

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), toastMessage, Toast.LENGTH_SHORT).show();
        }
    });
}

Solution 2: Using Handler

Another approach to fix this error is by using a Handler. A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Here’s how to use a Handler to display a Toast message:

public void showToastFromBackgroundThread() {
    final String toastMessage = "Hello, World!";
    final Context context = getApplicationContext();

    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show();
        }
    });
}

FAQ

1. Why can’t I display a Toast message from a background thread?

Android requires that all UI-related tasks be executed on the main (UI) thread. Displaying a Toast message is a UI task, so attempting to display it from a background thread will cause an error.

2. What is Looper.prepare()?

Looper.prepare() sets up a looper for the current thread if one does not already exist. It initializes a MessageQueue for the thread, allowing it to receive and process messages.

3. Can I use Looper.prepare() to fix this error?

No, calling Looper.prepare() in a background thread will not resolve the error. It might suppress the error message, but the Toast message will not be displayed since it is still not running on the main (UI) thread.

4. What are other ways to communicate between the main thread and background threads?

You can use AsyncTask, HandlerThread, or RxJava to communicate between the main thread and background threads in Android. Each has its use cases and advantages.

5. When should I use runOnUiThread vs. Handler?

Use runOnUiThread when you are already inside an Activity and need to execute a UI task on the main thread. Use a Handler when you need more fine-grained control over message processing or when you are outside of an Activity.

  • Android Developers: Toasts Overview
  • Android Developers: Threading Overview
  • Android Developers: AsyncTask
  • Android Developers: HandlerThread
  • RxJava in Android

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

Соизвольте помочь мне, есть тривиальная задача: создать поток который будет принимать и обробатывать сообщения через 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() из кода, ошибки не будет а код ваполнится в основном потоке

Solution 1

This error occurs because the default scheduler returned by AndroidSchedulers.mainThread() is an instance of LooperScheduler and relies on Android dependencies that are not available in JUnit tests.

We can avoid this issue by initializing RxAndroidPlugins with a different Scheduler before the tests are run. You can do this inside of a @BeforeClass method like so:

@BeforeClass
public static void setUpRxSchedulers() {
    Scheduler immediate = new Scheduler() {
        @Override
        public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
            // this prevents StackOverflowErrors when scheduling with a delay
            return super.scheduleDirect(run, 0, unit);
        }

        @Override
        public Worker createWorker() {
            return new ExecutorScheduler.ExecutorWorker(Runnable::run);
        }
    };

    RxJavaPlugins.setInitIoSchedulerHandler(scheduler -> immediate);
    RxJavaPlugins.setInitComputationSchedulerHandler(scheduler -> immediate);
    RxJavaPlugins.setInitNewThreadSchedulerHandler(scheduler -> immediate);
    RxJavaPlugins.setInitSingleSchedulerHandler(scheduler -> immediate);
    RxAndroidPlugins.setInitMainThreadSchedulerHandler(scheduler -> immediate);
}

Or you can create a custom TestRule that will allow you to reuse the initialization logic across multiple test classes.

public class RxImmediateSchedulerRule implements TestRule {
    private Scheduler immediate = new Scheduler() {
        @Override
        public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
            // this prevents StackOverflowErrors when scheduling with a delay
            return super.scheduleDirect(run, 0, unit);
        }

        @Override
        public Worker createWorker() {
            return new ExecutorScheduler.ExecutorWorker(Runnable::run);
        }
    };

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                RxJavaPlugins.setInitIoSchedulerHandler(scheduler -> immediate);
                RxJavaPlugins.setInitComputationSchedulerHandler(scheduler -> immediate);
                RxJavaPlugins.setInitNewThreadSchedulerHandler(scheduler -> immediate);
                RxJavaPlugins.setInitSingleSchedulerHandler(scheduler -> immediate);
                RxAndroidPlugins.setInitMainThreadSchedulerHandler(scheduler -> immediate);

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

Which you can then apply to your test class

public class TestClass {
    @ClassRule public static final RxImmediateSchedulerRule schedulers = new RxImmediateSchedulerRule();

    @Test
    public void testStuff_stuffHappens() {
       ...
    }
}

Both of these methods will ensure that the default schedulers will be overridden before any of the tests execute and before AndroidSchedulers is accessed.

Overriding the RxJava schedulers with an immediate scheduler for unit testing will also make sure the RxJava usages in the code being tested gets run synchronously, which will make it much easier to write the unit tests.

Sources:
https://www.infoq.com/articles/Testing-RxJava2
https://medium.com/@peter.tackage/overriding-rxandroid-schedulers-in-rxjava-2-5561b3d14212

Solution 2

I just added

RxAndroidPlugins.setInitMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());

in @Before annoted method.

Solution 3

I was getting the same error when testing LiveData. When testing LiveData, this InstantTaskExecutorRule is needed in addition to RxImmediateSchedulerRule if the class being tested has both background thread and LiveData.

@RunWith(MockitoJUnitRunner::class)
class MainViewModelTest {

    companion object {
        @ClassRule @JvmField
        val schedulers = RxImmediateSchedulerRule()
    }

    @Rule
    @JvmField
    val rule = InstantTaskExecutorRule()

    @Mock
    lateinit var dataRepository: DataRepository

    lateinit var model: MainViewModel

    @Before
    fun setUp() {
      model = MainViewModel(dataRepository)
    }

    @Test
    fun fetchData() {
      //given    
      val returnedItem = createDummyItem()    
      val observer = mock<Observer<List<Post>>>()    
      model.getPosts().observeForever(observer)    
      //when    
      liveData.value = listOf(returnedItem)    
      //than    
      verify(observer).onChanged(listOf(Post(returnedItem.id, returnedItem.title, returnedItem.url)))
    }

}

Reference:
https://pbochenski.pl/blog/07-12-2017-testing_livedata.html

Solution 4

Based on @starkej2 answer, with some changes, the correct answer for Kotlin developers would be:

  1. Create RxImmediateSchedulerRule.kt class:

,

import io.reactivex.Scheduler
import io.reactivex.android.plugins.RxAndroidPlugins
import io.reactivex.internal.schedulers.ExecutorScheduler
import io.reactivex.plugins.RxJavaPlugins
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import java.util.concurrent.Executor

class RxImmediateSchedulerRule : TestRule {
    private val immediate = object : Scheduler() {
        override fun createWorker(): Worker {
            return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
        }
    }

    override fun apply(base: Statement, description: Description): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                RxJavaPlugins.setInitIoSchedulerHandler { immediate }
                RxJavaPlugins.setInitComputationSchedulerHandler { immediate }
                RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate }
                RxJavaPlugins.setInitSingleSchedulerHandler { immediate }
                RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate }

                try {
                    base.evaluate()
                } finally {
                    RxJavaPlugins.reset()
                    RxAndroidPlugins.reset()
                }
            }
        }
    }
}
  1. On your test class, create schedulers ClassRule:

    class TestViewModelTest {
    
    companion object {
       @ClassRule
       @JvmField
       val schedulers = RxImmediateSchedulerRule()
    }
    
    @Before
    fun setUp() {
        //your setup code here
    }
    
    @Test
    fun yourTestMethodHere{}
    }
    

Solution 5

As in the advice in this Medium article by Peter Tackage you can inject the Schedulers yourself.

We all know that directly calling static methods can make for classes that are hard to test and if you use a dependency injection framework like Dagger 2 injecting the Schedulers can be especially easy. The example is as follows:

Define an interface in your project:

public interface SchedulerProvider {
    Scheduler ui();
    Scheduler computation();
    Scheduler io();
    Scheduler special();
    // Other schedulers as required…
}

Define an implementation:

final class AppSchedulerProvider implements SchedulerProvider {
    @Override 
    public Scheduler ui() {
        return AndroidSchedulers.mainThread();
    }
    @Override 
    public Scheduler computation() {
        return Schedulers.computation();
    }
    @Override 
    public Scheduler io() {
        return Schedulers.io();
    }
    @Override 
    public Scheduler special() {
        return MyOwnSchedulers.special();
    }
}

Now instead of using direct references to the Schedulers like this:

 bookstoreModel.getFavoriteBook()
               .map(Book::getTitle)
               .delay(5, TimeUnit.SECONDS)
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe(view::setBookTitle));

You use references to your interface:

bookstoreModel.getFavoriteBook()
          .map(Book::getTitle)
          .delay(5, TimeUnit.SECONDS, 
                 this.schedulerProvider.computation())
          .observeOn(this.schedulerProvider.ui())
          .subscribe(view::setBookTitle));

Now for your tests, you could define a TestSchedulersProvider like this:

public final class TestSchedulersProvider implements SchedulerProvider {

      @Override
      public Scheduler ui() {
          return new TestScheduler();
      }

      @Override
      public Scheduler io() {
          return Schedulers.trampoline(); //or test scheduler if you want
      }

      //etc
}

You now have all of the advantages of using TestScheduler when you want to in your unit tests. This comes in handy for situations where you might want to test a delay:

@Test
public void testIntegerOneIsEmittedAt20Seconds() {
    //arrange
    TestObserver<Integer> o = delayedRepository.delayedInt()
            .test();

    //act
    testScheduler.advanceTimeTo(20, TimeUnit.SECONDS);

    //assert
    o.assertValue(1);
}

Otherwise, if you don’t want to use injected Schedulers the static hooks mentioned in the other methods can be done using lambdas:

@Before
public void setUp() {
    RxAndroidPlugins.setInitMainThreadSchedulerHandler(h -> Schedulers.trampoline());
    RxJavaPlugins.setIoSchedulerHandler(h -> Schedulers.trampoline());
//etc
}

Comments

  • I am encountering a RuntimeException when attempting to run JUnit tests for a presenter that is using observeOn(AndroidSchedulers.mainThread()).

    Since they are pure JUnit tests and not Android instrumentation tests, they don’t have access to Android dependencies, causing me to encounter the following error when executing the tests:

    java.lang.ExceptionInInitializerError
        at io.reactivex.android.schedulers.AndroidSchedulers$1.call(AndroidSchedulers.java:35)
        at io.reactivex.android.schedulers.AndroidSchedulers$1.call(AndroidSchedulers.java:33)
        at io.reactivex.android.plugins.RxAndroidPlugins.callRequireNonNull(RxAndroidPlugins.java:70)
        at io.reactivex.android.plugins.RxAndroidPlugins.initMainThreadScheduler(RxAndroidPlugins.java:40)
        at io.reactivex.android.schedulers.AndroidSchedulers.<clinit>(AndroidSchedulers.java:32)
        …
    Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
        at android.os.Looper.getMainLooper(Looper.java)
        at io.reactivex.android.schedulers.AndroidSchedulers$MainHolder.<clinit>(AndroidSchedulers.java:29)
        ...
    
    
    java.lang.NoClassDefFoundError: Could not initialize class io.reactivex.android.schedulers.AndroidSchedulers
        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)
        …
    

  • pls also check my answer for a small change which might be required if you run into StackOverflowError

  • Thanks! I actually ran into that same issue shortly after posting my answer. Had a similar solution as you…I’ll update the answer since this info is probably useful for people.

  • Update: You can use the RxJavaHooks methods to set the schedulers. Also you have the TestScheduler available, as well as simply using Schedulers.immediate()

  • @NelsonRamirez I believe RxJavaHooks was removed in RxJava 2 and it’s functionality is now incorporated into RxJavaPlugins

  • kotlin and @JvmField, you save me :)

  • So i did something similar in a Kotlin project and it works but for whatever reason in a Java project the unit tests will only fail if you run it through ./gradlew test but it passes when you click run in the gutter next to the test class name in Android Studio. I thought it runs the test the same way. Does anyone know about this nuance ?

  • Don’t forget to check that your dependencies are all either androidx or all android.arch / com.android. You can’t mix and match and you will lose a lot of time wondering why it’s not working =)

  • This is the best answer, static hooks look like hack to me. Dependency injection is your friend

  • This worked for me when I had only 1 test that used Schedulers but it failed if I had multiple schedulers. The accepted answer works though

  • this worked for me after adding the below rule. @get:Rule var rule: TestRule = InstantTaskExecutorRule()

  • This worked for me instantly without anything else required. Thank you!

  • This did it for me. None of the above solutions were good for me, as they don’t include the imports (as always), and as someone new to Android I don’t know the frameworks they reference to.

  • just using @Rule @JvmField val rule = InstantTaskExecutorRule() it saved my day. thank you!

Recents

Понравилась статья? Поделить с друзьями:
  • Longpoll vklongpoll vk ошибка
  • Lolzteam ошибка 502
  • Lol произошла непредвиденная ошибка авторизации
  • Lol ошибка при запуске приложения 0xc0000142
  • Lol ошибка подключения к серверу