Android:如何让后续例程等待ObjectAnimator?

时间:2016-07-02 21:06:38

标签: java android user-interface locking objectanimator

有关此问题的更详细版本,请参阅How to make MediaPlayer wait for ObjectAnimator (and generally control Android UI sequence)?谢谢。 - 代码阅读

我正在尝试构建一个以这个序列启动的Android应用程序:

  1. 在(TextView)中缩放其主视图;
  2. 视图完全放大后,发出声音;
  3. 发出声音后,执行主程序。
  4. 我首先尝试在程序中的声音代码(ObjectAnimator)之前输入缩放代码(MediaPlayer)。但代码不按外观顺序运行。无论代码顺序如何,MediaPlayer都与ObjectAnimator并行播放。 为什么会这样,我该如何预防呢?

    尝试应对,我已经应用了各种计时和锁定方法,以使MediaPlayer在ObjectAnimator上等待。我在这里和其他地方搜索了很多解决方案。到目前为止,我发现的只有:

    1. Handler().postDelayed
    2. 运行MediaPlayer
    3. 动画完成后AnimatorListenerAdapter()启动MediaPlayer。
    4. 这两者都有效,但(1)并不精确,因为我必须硬编码等待的时间(并手动管理这个值),并且(2)不是一般的和主要的应用程序代码仍然在MediaPlayer完成之前启动。许多答案建议使用AsyncTask来控制Android UI序列,但是AsyncTask仅适用于两个任务,在后台线程中等待一个,这对于两个以上阶段的序列来说是不合适的。

      Android - wait for animation to finish before continuing?也是这个问题。接受的答案是AnimatorListenerAdaptor(),但OP问,现在有效,但如果我想在动画完成后再运行更多行代码,我必须基本上将我的整个代码放在onAnimationEnd方法? 我同意 - 这似乎不合理。

      通过进一步测试,我的问题似乎可以简化为:如何让后续例程等待ObjectAnimator?

      下面的代码说明了后一个问题。布尔waitLock设置为true。然后启动ObjectAnimator。然后while()循环等待waitLock被onAnimationEnd()设置为false。 ObjectAnimator永远不会完成,waitLock永远不会设置为false,而while()会无限循环。为什么呢?

      但是,如果waitLock最初设置为false,则ObjectAnimator完成并且应用程序正常退出。为什么ObjectAnimator在这种情况下完成而在另一种情况下不完整?

      public class MainActivity extends Activity {
      private boolean waitLock;
      View mainView;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          mainView = findViewById(R.id.mainView);
          waitLock = true;
          ObjectAnimator scalexAnim = ObjectAnimator.ofFloat(mainView, "scaleX", 0f, 1f);
          scalexAnim.setDuration(1500);
          scalexAnim.addListener(new AnimatorListenerAdapter() {
              @Override
              public void onAnimationStart(Animator animator) {
                  Log.w("sA", "onAnimationStart reached");
                  super.onAnimationStart(animator);
              }
              @Override
              public void onAnimationEnd(Animator animation) {
                  Log.w("sA", "onAnimationEnd reached");
                  waitLock = false;
                  super.onAnimationEnd(animation);
              }
          });
      
          Log.w("zI", "starting.."); zoomIn.start(); Log.w("zI", "started");
      
          while (waitLock == true) {
              SystemClock.sleep(1000);
              Log.w("while(waitLock)", "sleep 1000ms");
          }
          Log.w("onCreate", "exit");
      }
      

      waitLock设置为true时,以下是相应的logcat输出:

      07-02 13:34:14.850    9233-9233/com.code_read.sequentialcontroltest W/sA﹕ starting..
      07-02 13:34:14.860    9233-9233/com.code_read.sequentialcontroltest W/sA﹕ onAnimationStart reached
      07-02 13:34:14.860    9233-9233/com.code_read.sequentialcontroltest W/sA﹕ started
      07-02 13:34:15.861    9233-9233/com.code_read.sequentialcontroltest W/while(waitLock)﹕ sleep 1000ms
      07-02 13:34:16.862    9233-9233/com.code_read.sequentialcontroltest W/while(waitLock)﹕ sleep 1000ms
      07-02 13:34:17.863    9233-9233/com.code_read.sequentialcontroltest W/while(waitLock)﹕ sleep 1000ms
      07-02 13:34:18.864    9233-9233/com.code_read.sequentialcontroltest W/while(waitLock)﹕ sleep 1000ms
      

1 个答案:

答案 0 :(得分:0)

由于没有人回答这个问题,I'm posting what I've found so far。但是, 我认为这个问题仍然存在 。我当然不知道所有关于Android或Java的知识。我希望有更好的答案。因此,请随时张贴您自己的答案。我的发现是:

  1. 通常,Android平台和本机库不是 对同步编程友好。我找到了很多 问题(见下面的链接)询问如何进行各种操作 以受控的顺序发生,并没有非常令人满意的一般 这样做的方法;

  2. AsyncTask仅限于一对事件,无法在应用程序中重复使用;

  3. LooperHandler看起来很有希望一段时间,但显然Looper并没有等到事件结束才能将其他事件出局;

  4. 看起来视觉例程比音频例程更容易同步,可能是因为Android的可视化库调用比音频更成熟。我发现许多评论表明在同步Android的音频例程(例如MediaPlayerAudioTrack时出现问题。通常,时间很重要的推荐解决方案是使用已编译的C代码和专用库;

  5. 锁定机制适合单独的线程使用。我试图将它们用作控制单个线程中序列的方法总是失败并出现死锁;

  6. 相反,只有UI线程中的代码才能改变屏幕或生成声音。这导致了一个矛盾,我找到的唯一解决方案是严格控制UI线程的Looper队列,或者在音频和可视函数调用之间编码交错;

  7. 在Android“生命周期”中我放置的代码没有区别。例如,将onCreate()之后执行的代码移动到onResume()没有任何区别。

  8. 鉴于上述情况,下面是我对动画视图的原始问题的编码解决方案,然后发出声音,然后运行程序的其余部分。它依赖于回调,这会引起奇怪的反转:源文件中较早的 代码会在稍后的时间运行。 我从易读性和可管理性的角度来看这令人不安。我也不喜欢这个通常错综复杂的结构,但到目前为止,这个编码就像我所知道的那样简单。

    (顺便说一下:我怀疑这些同步问题是由单核CPU掩盖的。现在大多数Android硬件都是多核的,操作系统可能有一些赶上来做。我希望看到类似的东西@Synchronous 类,它将指定代码段按照它们出现的顺序串行运行。或者可以指定代码段只在特定内核上运行。

    链接到许多相关问题中的一些:

    我的编码解决方案。注意执行顺序是如何反转的。 rotateZoomin()首先运行,然后运行MediaPlayer(),然后运行playTracks():

    MediaPlayer.OnCompletionListener smComplete = new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mediaPlayer) {
            startupMediaPlayer.stop();
            startupMediaPlayer.reset();
            startupMediaPlayer.release();
            playTracks();    // This is third to execute (the rest of my application)
        }
    };
    
    startupMediaPlayer = MediaPlayer.create(this, R.raw.doorbell2mp3);
    startupMediaPlayer.setOnCompletionListener(smComplete);
    
    ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(mainView, "rotation", 1080f, 0f);
    ObjectAnimator scalexAnim = ObjectAnimator.ofFloat(mainView, "scaleX", 0f, 1f);
    ObjectAnimator scaleyAnim = ObjectAnimator.ofFloat(mainView, "scaleY", 0f, 1f);
    AnimatorSet rotateZoomIn = new AnimatorSet();
    rotateZoomIn.play(rotateAnim).with(scalexAnim);
    rotateZoomIn.play(rotateAnim).with(scaleyAnim);
    rotateZoomIn.setDuration(1500);
    rotateZoomIn.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            startupMediaPlayer.start();  // This is second to execute
        }
    });
    rotateZoomIn.start();  // This is first to execute