post方法到底能做什么?


106

我遇到了一个很奇怪的功能。

当我尝试在主线程上运行动画时,它无法启动。当我使用运行动画时

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

它确实开始。

CurrentThread在开始播放动画之前,我已经打印了,并且同时打印main

显然,我在这里丢失了一些东西,因为两者都应该在主线程上启动动画...我的猜测是,随着post将任务添加到队列中,它会在更“正确的时间”开始,但是我很想知道这里发生的事情更深入。

编辑:让我澄清一下-我的问题是,当在主线程上启动动画时,为什么在后期启动动画会导致动画启动。


这是特定于Android版本的行为吗?我无法在Android 4.1.2上重现它!
Akdeniz

我在Android 2.3.3上重现了此行为。但是为了AnimationDrawable!普通Animation实例开始在每个设置上成功制作动画。在AnimationDrawable情况; 当您尝试在中启动它时onCreate,由于当时未附加在视图上而无法启动。因此,这不是的线程问题AnimationDrawable。也许同样的事情适用Animationdeveloper.android.com/guide/topics/graphics/...
阿克登尼基

Answers:


161

post:post导致将Runnable添加到消息队列中,

Runnable:表示可以执行的命令。通常用于在其他线程中运行代码。

run() :开始执行该类代码的活动部分。当启动使用实现Runnable的类创建的线程时,将调用此方法。

getView().post(new Runnable() {

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

代码getView().startAnimation(a);

在您的代码中

post导致Runnable(代码将在另一个线程中运行)添加消息队列。

因此,当从messageQueue中获取startAnimation时,它将在新线程中触发

[编辑1]

为什么我们使用新线程而不是UI线程(主线程)?

UI线程:

  • 启动应用程序时,会自动创建Ui Thread

  • 它负责将事件调度到适当的小部件,其中包括绘图事件。

  • 这也是您与Android小部件进行交互的线程

例如,如果您触摸屏幕上的a按钮,则UI线程将touch事件调度到窗口小部件,窗口小部件依次设置其按下状态并将无效请求发布到事件队列。UI线程使请求出队,并通知小部件重新绘制自身。

如果用户按下将执行longOperation的按钮会怎样?

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

UI冻结。该程序甚至可能崩溃。

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

它打破了永远不会直接从工作线程更新UI的android规则

Android提供了几种从其他线程访问UI线程的方法。

  • Activity.runOnUiThread(Runnable)
  • View.post(可运行)
  • View.postDelayed(可运行,长)
  • 处理程序

像下面一样

View.post(可运行)

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

处理程序

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

在此处输入图片说明

欲了解更多信息

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

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


15
那么,为什么当最终都在同一线程上运行时,为什么在后期启动动画与在主线程上运行动画有所不同?
加勒2012年

因为此单线程模型可能会在Android应用程序中产生较差的性能。
塔拉(Talha)2012年

1
表现不佳与不显示动画有什么关系?
Gal

17
我不认为这可以回答问题,这更像是对ui线程和多线程一无所知的初学者的通用答案。这不能解释为什么将动画放在队列中才能使动画工作;动画应该可以直接在ui线程中执行,而无需使用任何post()或runOnUiThread()技巧。
carrizo

3
所有UI工作均应在主线程(UI线程)上进行。通过使用post()而不是立即在主线程中调用来使动画起作用的窍门是时间:如果您立即在主线程中调用,则意味着您告诉“立即开始动画”,但是此时可能是视图尚未准备好用于动画(测量,绘制...)。但是,如果将其放在post()中,它将在某个时候将队列中的startAnimation挂起,以准备为动画准备的视图。
NguyenDat

35

这是在onCreate或onCreateView上完成的吗?如果是这样,则该应用程序可能不会处于将视图附加到窗口的状态。许多基于View指标的算法可能无法正常工作,因为可能尚未计算出诸如View的尺寸和位置之类的信息。Android动画通常要求它们通过UI数学运行

View.post实际上将动画排队在View的消息循环中,因此,一旦视图附加到窗口,它将执行动画,而不是手动执行。

您实际上是在UI线程上运行东西,但时间不同


张贴者指出“帖子导致Runnable(代码将在其他线程中运行)”时,所接受的答案具有误导性。这是正确的答案“您实际上是在UI线程上运行东西,但是在不同的时间”-加1
smitty1

18

看看这里的一个很好的答案。view.post()与handler.post()几乎相同。它进入主线程队列,并在其他未完成的任务完成后执行。如果您调用activity.runOnUiThread(),它将在UI线程上立即被调用。


31
我发现一个巨大的(非常有用的)区别是view.post()中的runnable在首次显示View时被调用。IE,您可以将其设置为在View膨胀后开始动画,然后在将来的某个时候将其最终添加到View层次结构中。此时,动画将执行,您不必担心它。
DeeV

实际上,handler.post()并不总是在主线程上发布消息/可运行。这取决于处理程序的创建方式(可以将其与另一个线程上的Looper关联)。另一方面,view.post()将始终在主线程上运行
Yair Kukielka,

4

我认为问题可能是您调用post()方法的生命周期方法。您是否正在onCreate()中进行此操作?如果是这样,请查看我在活动的onResume()文档中找到的内容:

onResume()

在API级别1中添加的void onResume()在onRestoreInstanceState(Bundle),onRestart()或onPause()之后调用,以使您的活动开始与用户进行交互。这是开始动画,打开独占访问设备(例如相机)等的好地方

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

因此,正如乔·普兰特(Joe Plante)所说,也许在您调用post()时,视图尚未准备好开始动画,因此请尝试将其移至onResume()。

PD:实际上,如果确实将代码移到onResume()上,那么我认为您可以删除post()调用,因为您已经在ui线程中,并且视图应该可以开始动画了。


3
onResume最初在“视图准备就绪”之后,可能会被多次调用(屏幕进入睡眠状态,活动被推送到后退堆栈等)。如果从调用onResume,则可能需要标记来跟踪动画已经开始的天气,以避免(重新)开始多次。
samis
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.