ScheduledThreadPoolExecutor执行莫名停止问题&Android几个动画回调运行线程

本文记录两个问题:

  • ScheduleThreadPoolExecutor莫名停止执行。
  • Animation和Animator两个动画回调监听 运行在哪个线程。

一:ScheduleThreadPoolExecutor问题:

ScheduledThreadPoolExecutor中scheduleWithFixedDelay(command, initialDelay, delay, unit)这个方法。我们会实现command这个Runnable参数。

问题:

当我们实现run()方法时,如果run()方法内运行时报错,那么ScheduleThreadPoolExecutor会自动停止,并且app不会崩溃,logcat也不会打印错误日志。。。

解决方法:

在api中也介绍了,如果希望executor继续运行下去,那么就必须将报错try catch掉。否则报错线程池将停止继续运行。

二:Animation和Animator两个动画回调监听 运行在哪个线程:

setAnimationListener(new …{
onAnimationStart()
onAnimationRepeat()
onAnimationEnd()
})

1.Animation:View动画

先找到这个回调调用的地方:在Animation类中,源码:

/**
     * Sets the handler used to invoke listeners.
     * 
     * @hide
     */
    public void setListenerHandler(Handler handler) {
        if (mListenerHandler == null) {
            mOnStart = new Runnable() {
                public void run() {
                    if (mListener != null) {
                        mListener.onAnimationStart(Animation.this);
                    }
                }
            };
            mOnRepeat = new Runnable() {
                public void run() {
                    if (mListener != null) {
                        mListener.onAnimationRepeat(Animation.this);
                    }
                }
            };
            mOnEnd = new Runnable() {
                public void run() {
                    if (mListener != null) {
                        mListener.onAnimationEnd(Animation.this);
                    }
                }
            };
        }
        mListenerHandler = handler;
    }

可以看见回调方法被放到了Runnable里,那么讲道理,这个会通过handler处理
那么找mOnStart。。这几个参数在哪里被调用了:

private void fireAnimationStart() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationStart(this);
            else mListenerHandler.postAtFrontOfQueue(mOnStart);
        }
    }

恩,将Runnable传递给mListenerHanlder去处理了。那么mListenerHandler这个handler在哪里被赋值的呢?哦。。在上面方法最后

  mListenerHandler = handler;

最后找到,在View类中:其中有一句代码:

if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);

A Handler supplied by a view’s {@link android.view.ViewRootImpl}. This handler can be used to pump events in the UI events queue. 
这是AttachInfo类中mHandler的注释。可以看到是管UI的handler。那肯定是在主线程中执行回调方法了。


结论:Animation回调方法一定是在主线程中了。

2.Animator:属性动画ValueAnimator等

同样,看listener在源码哪里被调用了:

private void notifyStartListeners() {
        if (mListeners != null && !mStartListenersCalled) {
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
            int numListeners = tmpListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationStart(this);
            }
        }
        mStartListenersCalled = true;
    }
 private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        ...
        if (mStartDelay == 0) {
            ...
            notifyStartListeners();
        }
        animationHandler.start();
    }

      Animator的start()会调用上面start(boolean)方法,调用notifyStartListeners()方法。这么看来,意思是Animator.start()方法在哪个线程里执行,回调就在哪个线程中。一会儿咱们测试一下是不是。注意上面一句代码:

AndroidRuntimeException("Animators may only be run on Looper threads");

看来Animator是可以在子线程中运行,但是该线程必须有looper!好咧,搞个代码测试下!

final ValueAnimator va = ObjectAnimator.ofFloat(0, 1);
		va.setDuration(5000);
		va.addUpdateListener(new AnimatorUpdateListener() {

			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				 Log.e("TAG", "onAnimationUpdate"
				 + Thread.currentThread().getName());
				// start在什么线程回调就在什么线程
			}
		});
		va.addListener(new AnimatorListener() {
			@Override
			public void onAnimationStart(Animator animation) {
				Log.e("TAG", "onAnimationStart"
						+ Thread.currentThread().getName());
			}

			@Override
			public void onAnimationRepeat(Animator animation) {
				Log.e("TAG", "onAnimationRepeat"
						+ Thread.currentThread().getName());
			}

			@Override
			public void onAnimationEnd(Animator animation) {
				Log.e("TAG", "onAnimationEnd"
						+ Thread.currentThread().getName());
			}

			@Override
			public void onAnimationCancel(Animator animation) {
				Log.e("TAG", "onAnimationCancel"
						+ Thread.currentThread().getName());
			}
		});
<pre name="code" class="java">//va.start();

new Thread(“hah”) {public void run() {Looper.prepare();va.start();Looper.loop();};}.start();

上面代码测试后,结果果然是当start()在主线程中调用,那么回调方法全部都在主线程。如果在“hah”这个子线程中调用,那么回调方法都在“hah”子线程中。

结论:Animator的start()方法在哪个线程中调用,回调就在哪个线程中。注意:子线程需要有looper。

    原文作者:AimSunshine
    原文地址: https://blog.csdn.net/u013568090/article/details/52453276
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞