What exactly does the post method do?
Asked Answered
A

4

140

I've encountered a very weird feature.

When I'm trying to run an animation on the main thread, it does not start. When I run said animation using

getView().post(new Runnable() {
            @Override
            public void run() {
                getView().startAnimation(a);
            }
        });

It does start.

I've printed the CurrentThread before starting the animation and both print main.

Obviously, I am missing something here, as both should start the animation on the main thread... My guess is that as post adds the task to the queue, it starts at a more "correct time", but I would love to know what happens here at more depth.

EDIT: Let me clear things up - my question is, why starting the animation on post causes it to start, when starting the animation on the main thread does not.

Agleam answered 12/12, 2012 at 12:39 Comment(2)
Is this behavior specific to an Android version? I could not reproduce it on Android 4.1.2!Azilian
I reproduced this behavior on Android 2.3.3. But for AnimationDrawable! Ordinary Animation instance started to animate successfully on each setup. In AnimationDrawable case; when you try to start it in onCreate, it dont start because of not being attached to view at that moment. So it is not a threading issue for AnimationDrawable. Maybe same thing applies for Animation? developer.android.com/guide/topics/graphics/…Azilian
L
180

post :post causes the Runnable to be added to the message queue,

Runnable : Represents a command that can be executed. Often used to run code in a different Thread.

run () : Starts executing the active part of the class' code. This method is called when a thread is started that has been created with a class which implements Runnable.

getView().post(new Runnable() {

         @Override
         public void run() {
             getView().startAnimation(a);
         }
     });

code : getView().startAnimation(a);

in your code,

post causes the Runnable (the code will be run a in different thread) to add the message queue.

So startAnimation will be fired in a new thread when it is fetched from the messageQueue

[EDIT 1]

Why do we use a new thread instead of UI thread (main thread)?

UI Thread :

  • When application is started, Ui Thread is created automatically

  • it is in charge of dispatching the events to the appropriate widgets and this includes the drawing events.

  • It is also the thread you interact with Android widgets with

For instance, if you touch the a button on screen, the UI thread dispatches the touch event to the widget which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget to redraw itself.

What happens if a user press a button which will do longOperation ?

((Button)findViewById(R.id.Button1)).setOnClickListener(           
             new OnClickListener() {        
        @Override
    public void onClick(View v) {
            final Bitmap b = loadImageFromNetwork();
            mImageView.setImageBitmap(b);
}
});

The UI freezes. The program may even crash.

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        final Bitmap b = loadImageFromNetwork();
        mImageView.setImageBitmap(b);
    }
  }).start();
}

It breaks the android rule that never update UI directly from worker thread

Android offers several ways to access the UI thread from other threads.

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

Like below,

View.post(Runnable)

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}

Handler

final Handler myHandler = new Handler(Looper.getMainLooper());

(new Thread(new Runnable() {

    @Override
    public void run() {
       final Bitmap b = loadImageFromNetwork();
      myHandler.post(new Runnable() {                           

        @Override
        public void run() {
           mImageView.setImageBitmap(b);
          }
        });
      }
    })).start();                
}

enter image description here

For more info

http://android-developers.blogspot.com/2009/05/painless-threading.html

http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/

Lactary answered 12/12, 2012 at 12:56 Comment(15)
So why does starting the animation on post is different than running it on the main thread, when they both eventually run on the same thread?Agleam
Because this single thread model can yield poor performance in Android applications.Lactary
What does poor performance has to do with not showing an animation?Agleam
Dont think this only about startanimation. It means, " dont do it in UIThread, do it in new thread. "Lactary
I know all about multi-threading, and multi-thread in UI; my question is why does posting the start animation, considering it still runs on the main thread, causes it to show.Agleam
Although I did not know you could create a new Handler with no contextAgleam
So, you know your question's answer yourself i think. It post because, dont want to block main thread, it uses "post" because need to update view from new task after complate.Lactary
startAnimation does not block the UI thread, nor is the thread blocked by calling startAnimation (the animation is never shown). this also doesn't happen with all animations. I could find nothing in the API suggesting that startAnimation needs to be run by post, if that's what you mean.Agleam
let us continue this discussion in chatLactary
When invoke View.post(Runnable r), r will be executed on UI threadStepfather
I don't think this answers the question, this is more like a generic answer for beginners who doesn't know anything about the ui-thread and multi threading. This doesn't explains why throwing the animation ahead in the queue makes the animation work; an animation is supposed to be something to execute directly in the ui-thread without using any post() or runOnUiThread() tricks.Chicane
@Chicane ok, you can write a better answer :)Lactary
@Chicane It might happen because of a different explanation too. Maybe before your runnable started to run the lifecycle of the activity/fragment has changed? Don't forget you entered the msg into the looper's messageQueue and you don't know exactly when it's going to be executed. Another very possible solution is that you found a bug in the Android OS.Ecumenicist
All UI work should be on Main thread (UI thread). The trick that make animation work by using post() instead of call in main thread right away is Time: If you call right away in main thread that mean you told "start animation now", But at this time may be view is not ready for animation (measure, draw...). But If you put that in post(), It will pending startAnimation in queue sometime for prepare view ready for anim.Camp
This answer is partially incorrect, when you call view.post you're indeed posting a new runnable to a message queue. However, the documentation states it clearly that the runnable will be invoked on the UI thread and not a new thread.Zilla
L
61

Is this being done on onCreate or onCreateView? If so, the app might not be in a state where the View is attached to the window. A lot of algorithms based on View metrics may not work since things like the View's measurements and position may have not been calculated. Android animations typically require them to run through UI math

View.post actually queues the animation on the View's message loop, so once the view gets attached to the window, it executes the animation instead of having it execute manually.

You are actually running things on the UI thread, but at a different time

Legendary answered 22/10, 2014 at 15:54 Comment(2)
The accepted answer is misleading where the poster states "post causes the Runnable (the code will be run a in different thread)". This is the correct answer "You are actually running things on the UI thread, but at a different time" - plus 1Clitoris
Yes, the Runnable (code) is posted to a Handler which is (assumed) to be associated with the UI(main) thread. It is queued up until the UI thread has finished initializing everything else, then it is executed. It is being used in effect as a job queuer. Accepted answer has missed the point.Orle
C
23

Have a look here for a good answer. view.post() is the same as handler.post() pretty much. It goes into the main thread queue and gets executed after the other pending tasks are finished. If you call activity.runOnUiThread() it will be called immediately on the UI thread.

Confucius answered 12/12, 2012 at 12:47 Comment(2)
One massive (and extremely helpful) difference I've found is the runnable in view.post() will be called when the View is first shown. I.E., you can set it to start an animation upon inflation of the View then at some point in the future, finally add it to the view hierarchy. At which point, then animation will execute and you won't have to worry about it.Cushy
Actually, handler.post() does not always post a message/runnable on the main thread. It depends on how the handler is created (it can be associated to a Looper on a different thread). On the other hand, view.post() will always run on the main threadMelendez
C
6

The problem I think could be the life-cycle method where you are calling the post() method. Are you doing it in onCreate()? if so look at what I found in the activity's onResume() documentation:

onResume()

Added in API level 1 void onResume () Called after onRestoreInstanceState(Bundle), onRestart(), or onPause(), for your activity to start interacting with the user. This is a good place to begin animations, open exclusive-access devices (such as the camera), etc.

https://developer.android.com/reference/android/app/Activity.html#onResume()

So, as Joe Plante said, maybe the view is not ready to start animations at the moment you call post(), so try moving it to onResume().

PD: Actually if you do move the code to onResume() then I think you can remove the post() call since you are already in the ui-thread and the view should be ready to start animations.

Chicane answered 15/11, 2016 at 6:9 Comment(1)
onResume may be called multiple times (screens goes to sleep, activity pushed to backstack, etc...) after it is initially when "the view is ready". If called from onResume, then a flag may be needed to track weather the animation has already been started, to avoid (re)starting multiple times.Innsbruck

© 2022 - 2024 — McMap. All rights reserved.