本文记录两个问题:
- 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。