I was attempting to test a method which sets a photo inside an ImageView using Picasso when I stumbled upon an error. The error outputted to logcat was "Method call should happen from the main thread". It was thrown by Picasso, and it was causing my test to fail.
I searched the web to try and find guidance on resolving this error and found this issue in the Picasso github repo.
The developer Jake Wharton outlines the reasons why the exception is thrown, and why the constraint cannot be relaxed.
The issue reporter follows up stating how they resolved the issue by changing from a subclass of ActivityTestCase
to a subclass of InstrumentationTestCase
. In my case however, I am writing a unit test and the isolation that ActivityUnitTestCase
offers me is much closer to what I want to be working with than the environment in which an InstrumentationTestCase
runs.
I played around with.. everything.
I stumbled upon this StackOverflow question which proposes a solution which may well resolve your issues if you are testing a unit which only interacts with the UI thread.
Unfortunately my setup for unit testing requires a call to waitForIdleSync
which the documentation explicitly states can "not be called from the main application thread". As such universally wrapping my text execution in a runnable as suggested was not suitable.
I then stumbled upon this answer which unfortunately is a little vague and unclear. It did however get me thinking.
Handlers by default run on the thread on which they are created. I considered simply running the part of the code that needed to be executed on the main thread within a call to runOnUiThread
. This however was not working.. I believe because under the hood this method uses a Handler.
After a bit more thinking, Googleing, and testing I found a fix and an amazing Stack Overflow question which clarified and cleared up nearly all of my thoughts on the matter (read through all the answers, the comments etc - it is really interesting stuff).
HPP's comment which has now become an answer was the solution:
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//stuff to run on the main thread
}
});
This call explicitly sets the thread on which the code should be executed. In my case running my method within such a runnable made Picasso happy, allowed my test to pass, and allowed me to continue using ActivityUnitTestCase
.
Hopefully this will help someone. If you have anny questions or comments, feel free to ask :)