1

Title says it all. Well mostly. This is only happening on 1 device. Other devices are fine.

In the onPostExecute of a AsyncTask I am calling the code below, before getting this exception I did not have the runOnUiThread, the runnable was added because I am getting the CalledFromWrongThreadException exception.


EDIT

Here is what I have discovered. In certain circumstances an application can have more than one "looper" and therefore more than one "UI thread".

Since an application can have more than one "UI thread" and an AsyncTask always "Runs on the UI thread" [ref], someone decided [poorly] that instead of the AsyncTask always running on its creation thread (which in 99.999999% of cases would be the correct "UI thread") they decided to use hocus pocus (or a poorly crafted shortcut, you decide) to execute on the "main looper"..

Android: got CalledFromWrongThreadException in onPostExecute() - How could it be?

So it seems now I need to find a way to make sure the async tasks thread is always the looper thread the app is on :(


getParentActivity().runOnUiThread(new Runnable()
{
        @Override
        public void run()
        {
            setAdapterData(result);
        }
});

when that didn't work I tried this as well, and tried them together.

lvUpdatesList.post(new Runnable()
{
    @Override
    public void run()
    {
        lvUpdatesList.setAdapter(updateListAdapter);
    }
});

I am super confused as to why it is behaving like this.

07-03 13:19:51.908: E/AndroidRuntime(4479): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.view.ViewRoot.checkThread(ViewRoot.java:2932)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.view.ViewRoot.invalidateChild(ViewRoot.java:642)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:668)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.view.ViewGroup.invalidateChild(ViewGroup.java:2511)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.view.View.invalidate(View.java:5279)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.widget.AbsListView.resetList(AbsListView.java:1120)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.widget.ListView.resetList(ListView.java:511)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.widget.ListView.setAdapter(ListView.java:440)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at com.********.********.FragmentUpdates.setAdapterData(FragmentUpdates.java:213)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at com.********.********.FragmentUpdates$AsyncGetUpdates.onPostExecute(FragmentUpdates.java:185)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at com.********.********.FragmentUpdates$AsyncGetUpdates.onPostExecute(FragmentUpdates.java:1)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.os.AsyncTask.finish(AsyncTask.java:417)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.os.AsyncTask.access$300(AsyncTask.java:127)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.os.Looper.loop(Looper.java:130)
07-03 13:19:51.908: E/AndroidRuntime(4479):     at android.os.HandlerThread.run(HandlerThread.java:60)

full asynctask

class AsyncGetUpdates extends AsyncTask<Void, Void, List<UpdateDTO>>
    {

        String userId;

        @Override
        protected void onPreExecute()
        {
            showDialog();
            userId = getParentActivity().getCurrentUser().getUser_id();
            super.onPreExecute();
        }

        @Override
        protected List<UpdateDTO> doInBackground(Void... params)
        {
            boolean followingOnly = false;
            if (myState == MyState.Following)
                followingOnly = true;

            return APIHelper.getUpdates(userId, followingOnly, count);
        }

        @Override
        protected void onPostExecute(final List<UpdateDTO> result)
        {
            killDialog();
            isCurrentlyUpdating = false;
            getParentActivity().runOnUiThread(new Runnable()
            {
                @Override
                public void run()
                {
                    setAdapterData(result);
                }
            });
            super.onPostExecute(result);
        }

    }
Community
  • 1
  • 1
WIllJBD
  • 6,144
  • 3
  • 34
  • 44
  • 1
    `In the onPostExecute of a AsyncTask I am calling the code below` ? no need to use runOnUiThread in onPostExecute because onPostExecute always run on main ui thread. so just call `lvUpdatesList.setAdapter(updateListAdapter);` in onPostExecute for filling Adapter with data – ρяσѕρєя K Jul 03 '13 at 19:30
  • onPostExecute was already throwing the exception. I added runOnUiThread because onPostExecute was throwing the exception, and does throw it, with or without runOnUiThread. What you are suggesting I was doing from the start, and works fine on all the devides except for this HTC Nexus One 2.3.3 – WIllJBD Jul 03 '13 at 19:32
  • probably problem is in other part of code so plz show full `AsyncGetUpdates` class code to get more help from us – ρяσѕρєя K Jul 03 '13 at 19:34
  • what is line number 213 in FragmentUpdates.java ? – ρяσѕρєя K Jul 03 '13 at 19:38
  • lvUpdatesList.setAdapter(updateListAdapter); – WIllJBD Jul 03 '13 at 19:39
  • I think the real issue here is that on this one device, the `@Override protected void onPostExecute()` is not being called on the main thread. I am executing the task from the main thread, but it is not postExecuting to it. – WIllJBD Jul 03 '13 at 19:45
  • updated with some interesting info – WIllJBD Jul 03 '13 at 19:56

1 Answers1

2

For the future if people have this issue, which you wont in any post JellyBean devices, this is how you fix it.

The very very very first thing you do in your app is this.

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_splash);

    this.runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            new AsyncInitLooperTask().execute();
        }
    });
     }

public class AsyncInitLooperTask extends AsyncTask<Void, Void, Void>
{
    @Override
    protected Void doInBackground(Void... params)
    {
        return null;
    }
}

What this will do it attach the InternalHandler that deals with AsyncTasks for you app to the correct Looper/UIThread that your app is using.

WIllJBD
  • 6,144
  • 3
  • 34
  • 44