有关此问题的更详细版本,请参阅How to make MediaPlayer wait for ObjectAnimator (and generally control Android UI sequence)?谢谢。 - 代码阅读
我正在尝试构建一个以这个序列启动的Android应用程序:
我首先尝试在程序中的声音代码(ObjectAnimator)之前输入缩放代码(MediaPlayer)。但代码不按外观顺序运行。无论代码顺序如何,MediaPlayer都与ObjectAnimator并行播放。 为什么会这样,我该如何预防呢?
尝试应对,我已经应用了各种计时和锁定方法,以使MediaPlayer在ObjectAnimator上等待。我在这里和其他地方搜索了很多解决方案。到目前为止,我发现的只有:
Handler().postDelayed
和AnimatorListenerAdapter()
启动MediaPlayer。这两者都有效,但(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
答案 0 :(得分:0)
由于没有人回答这个问题,I'm posting what I've found so far。但是, 我认为这个问题仍然存在 。我当然不知道所有关于Android或Java的知识。我希望有更好的答案。因此,请随时张贴您自己的答案。我的发现是:
通常,Android平台和本机库不是 对同步编程友好。我找到了很多 问题(见下面的链接)询问如何进行各种操作 以受控的顺序发生,并没有非常令人满意的一般 这样做的方法;
AsyncTask仅限于一对事件,无法在应用程序中重复使用;
看起来视觉例程比音频例程更容易同步,可能是因为Android的可视化库调用比音频更成熟。我发现许多评论表明在同步Android的音频例程(例如MediaPlayer或AudioTrack时出现问题。通常,时间很重要的推荐解决方案是使用已编译的C代码和专用库;
锁定机制适合单独的线程使用。我试图将它们用作控制单个线程中序列的方法总是失败并出现死锁;
相反,只有UI线程中的代码才能改变屏幕或生成声音。这导致了一个矛盾,我找到的唯一解决方案是严格控制UI线程的Looper队列,或者在音频和可视函数调用之间编码交错;
在Android“生命周期”中我放置的代码没有区别。例如,将onCreate()之后执行的代码移动到onResume()没有任何区别。
鉴于上述情况,下面是我对动画视图的原始问题的编码解决方案,然后发出声音,然后运行程序的其余部分。它依赖于回调,这会引起奇怪的反转:源文件中较早的 代码会在稍后的时间运行。 我从易读性和可管理性的角度来看这令人不安。我也不喜欢这个通常错综复杂的结构,但到目前为止,这个编码就像我所知道的那样简单。
(顺便说一下:我怀疑这些同步问题是由单核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