一.View和Activity,Window之间的关系:
在构造Activity的时候呢,会初始化一个Window .(PhoneWindow),而PhoneWindow设置DecorView,作为窗口的根视图(Viewroot),分为TitleView和 ContextView。在OnCreated()方法中,setContextView()用来设置上面的方法,而findVIewbyid则是至上而 下的遍历这棵控件树(我们都知道findViewbyid这个方法是十分消耗内存的,对于性能的优化可以从这里入手考虑)。Viewroot通过 addview方法加入将Viwe添加进来(Textview,button,ect.), 而这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。
二.View的更新
Android更新view有两组方法,invalidate在UI线程中使用,postInvalidate在非线程中使用。
postInvalidate源码:
public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
if (mAttachInfo != null) {
Message msg = Message.obtain();
msg.what = AttachInfo.INVALIDATE_MSG;
msg.obj = this;
mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
}
可见,还是用过handle.sendMessageDelayed,最终交给了主线程UI处理的。
这个时候在说一下checkThread()方法:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
可见如果mThread !=Thread.currentThread就会抛出异常。而mThread是在构造方法中实例化的,为主线程,而Thread.currentThread()是当前UI。该代码出自 framework/base/core/java/android/view/ViewRootImpl.java
还需要一提的是invalidatechilid()和invalidatechildparent()方法:
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
...
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
...
}
方法中调用了checkThread(),还有关于ui的更新。
既然是单线程模型,就要先找到这个UI线程实现类ActivityThread,看里面哪里 addview 了。没错,是在onResume里面,对应ActivityThread就是handleResumeActivity这个方法:
<pre>final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true; // TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
...... if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit; if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
} // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible.
} else if (!willBeVisible) { if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
......
}
所以,如果在onCreate中通过子线程直接更新UI,并不会抛CalledFromWrongThreadException异常。但是一般情况下,我们不会在onCreate中做这样的事情。
- 这就是Android为我们设计的单线程模型,核心就是一句话:Android UI操作并不是线程安全的,并且这些操作必须在UI线程执行。
三.事件拦截:
上文提到过view的创建流程,android的事件拦截主要是三个方法:
1.dispatchtouchevent():进行事件分发,只要事件传递给当前view,此方法就一定会被调用。
2.onInterceptTouchEvent( ): 标记是否拦截Touch事件,如果拦截则自己处理,否则交给子View处理(可以是ViewGroup,也可以是View)。
3.onTouchEvent(): 在拦截之后进行事件的处理。
上一段开发艺术中的伪代码帮助理解:
public void ondispatchTouchevent(Motionevent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
这里提一下mFirstTouchTarget:即为事件处理ViewmFirstTouchTarget != null的情况下才会调用onInterceptTouchEvent(),所以当事件如果为Move并且mFirstTouchTarget == null 的情况下(ViewGroup没有处理当前事件的子View)就直接intercepted = true,表明自己拦截。
通过判断x,y来确定子view是否包含,如果包含就添加到mFirstTouchTarget中,否则继续遍历。
在拿一段view dispatchTouchEvent( )的关键部分:
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//onTouchEvent()
if (!result && onTouchEvent(event)) {
result = true;
}
}