从前文《源代码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?》中知道了activity第一个view或者说根view或者说mDecorView 事实上就是一个FrameLayout,以及是在系统handleResume的时候增加到系统windowManager中的,并由framework中的ViewRootImpl
接管,通过ViewRootImpl.setView() 開始整个显示过程的。

这次着重梳理一下view的显示过程(onAttach, onMeasure, onLayout, onDraw )在源代码中的过程。

从ViewRootImpl.setview 開始。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view; <span style="color:#ff0000;">requestLayout();</span>
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}

在第一次赋值mView的时候。会调用ViewRootImpl.requestLayout();

    public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}

进而scheduleTraversals();

    public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true; //noinspection ConstantConditions
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
final long now = System.nanoTime();
Log.d(TAG, "Latency: Scheduled traversal, it has been "
+ ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
+ "ms since the last traversal finished.");
} sendEmptyMessage(DO_TRAVERSAL);
}
}

进而在handleMessage() 中

    @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL: performTraversals();
<span style="white-space:pre"> </span>}

进而就是performTraversals(),也就是本次分析的重点。

这个函数比較长,不适合把所有函数代码都 贴上来。

就分段叙述。

1. 函数刚開始的部分,初始化了一些后面会用到的变量和标志位。

        final View host = mView;
WindowManager.LayoutParams lp = mWindowAttributes;
final View.AttachInfo attachInfo = mAttachInfo;
CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
Rect frame = mWinFrame;
mTraversalScheduled = false;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean fullRedrawNeeded = mFullRedrawNeeded;
boolean newSurface = false;
boolean surfaceChanged = false;

2.  接下来,是一个重要的推断, 假设是初次运行,则调用host.dispatchAttachedToWindow(attachInfo, 0);

        if (mFirst) {
// 略去一大堆赋值
mLastConfiguration.setTo(host.getResources().getConfiguration());
host.dispatchAttachedToWindow(attachInfo, 0);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); host.fitSystemWindows(mAttachInfo.mContentInsets); } else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}

在dispatchAttachedToWindow()中重点处理了三件事:

2.1. onAttachedToWindow();

2.2. listener.onViewAttachedToWindow(this);

2.3 onWindowVisibilityChanged(vis);

    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
mWindowAttachCount++;
onAttachedToWindow(); final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
mOnAttachStateChangeListeners;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
} int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
}
}

在这里onAttachedToWindow() 的凝视带来了一个问题:仅保证会在onDraw 前调用,而不保证在onMeasure 之前或者之后调用 onAttachedToWindow。

    /**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
* guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
* however it may be called any time before the first onDraw -- including
* before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
protected void onAttachedToWindow() {
    protected void onAttachedToWindow() {
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
resolveLayoutDirectionIfNeeded();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
}
}

3 fitSystemWindows(), 假设是系统window 则使用padding的值计算一下insets,并開始android.view.View.requestLayout()

    protected boolean fitSystemWindows(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
mPaddingLeft = insets.left;
mPaddingTop = insets.top;
mPaddingRight = insets.right;
mPaddingBottom = insets.bottom;
requestLayout();
return true;
}
return false;
}

在android.view.View.requestLayout()中,就是简单的一层一层向上检查parent是否存在,若存在调用parent的requestLayout();

    /**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree.
*/
public void requestLayout() {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
} mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED; if (mParent != null) {
if (mLayoutParams != null) {
mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
}
if (!mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
}

可是每层调用完毕。并非马上运行layout操作,而是通过赋值标志位mPrivateFlags |= FORCE_LAYOUT;,来标识一下而已。真正的layout过程在后面。

4. 有一段代码要说一下。

        if (mLayoutRequested && !mStopped) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);

RunQueue 是在handler 没有初始化的时候用来处理事件的消息队列。 把给ViewRootImpl post的事件 类型是runnable , 等到handler 构造好后。再发给handler 处理。不是本文的重点,就简单提一下。

5 接下来是measure 的部分

            boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(TAG, "Good!");
goodMeasure = true;
}
}
}
}
            if (!goodMeasure) {
                childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                    windowSizeMayChange = true;
                }
            }

这段代码基本就是在某些情况下 用特定的參数来measure , 另外一些情况下。用另外一些值来measure.

都是调用的host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 仅仅是情况不同,使用的參数不同。

计算值的这部分參见前文《源代码分析:LayoutParams的wrap_content, match_parent, 和详细值

然后就是调用measure()方法

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET; if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
} // measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec); // flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
} mPrivateFlags |= LAYOUT_REQUIRED;
} mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}

在一些推断和标志位mPrivateFlags 赋值后。调用onMeasure() 方法。 onMeasure() 的讨论也请移步前文《源代码分析:LayoutParams的wrap_content, match_parent, 和详细值

在方法的最后给标志位置位 

mPrivateFlags |= LAYOUT_REQUIRED;

6 接着一大堆复杂的逻辑和赋值之后。调用了relayoutWindow() 这个还不太明确是怎么回事 // TODO

                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

7 接着又是一堆逻辑和赋值,并在某种情况下还调用了host.measure()。算是measure 完毕了

8. 然后是layout 的部分。

        final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

在layout() 中,先setFrame,然后推断状态标志位,进而回调onLayout(); 也就是自己定义的部分。

    public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
} onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED; if (mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~FORCE_LAYOUT;
}

在setFrame()中,推断新的位置和旧的位置是否一致。

    protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false; if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
} if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true; // Remember our drawn bit
int drawn = mPrivateFlags & DRAWN; int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); // Invalidate our old position
invalidate(sizeChanged); mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom; mPrivateFlags |= HAS_BOUNDS; if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
} if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
} // Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn; mBackgroundSizeChanged = true;
}
return changed;
}

假设不一致,则调用invalidate();

    void invalidate(boolean invalidateCache) {
<span style="white-space:pre"> </span>if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}

调用父控件的p.invalidateChild() 来计算并标识 dirty 区域, 区域范围就是子view 所处的区域。

    public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}

假设setframe 的返回值 为true 。则表示 区域的内容发生了变化进而回调onLayout() 方法。和mOnLayoutChangeListeners的onLayoutChange()。

    /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

在onLayout() 中, view应该通过调用每个子view 的layout() 方法。来指定每个子view 的大小和位置,。

9 接着有个computesInternalInsets的部分不太懂,sWindowSession.setInsets // TODO

        if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset(); // Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}

10, 略去一部分处理过程。有焦点的回调和处理, 输入法的处理等。

11. 进入draw 的部分。

        if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false; final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
} draw(fullRedrawNeeded); if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
} if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}

在draw() 方法中,计算了dirty 的区域。 假设使用了硬件加速的话,进行对应的处理,否则使用canvas  来绘制view。

<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">    private void draw(boolean fullRedrawNeeded) {</span>
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"> mView.draw(canvas);</span>

在android.view.View.draw(Canvas)中。绘制全部的子类

    public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
} final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN; /*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/ // Step 1, draw the background, if needed
int saveCount; if (!dirtyOpaque) {
final Drawable background = mBGDrawable;
if (background != null) {
final int scrollX = mScrollX;
final int scrollY = mScrollY; if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
} if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
} // skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children
dispatchDraw(canvas); // Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas); // we're done...
return;
}

凝视写的非常明确。 在step 1 中绘制背景, 在step 3 中调用自己的ondraw(); 在step 4 中 调用dispatchDraw() 绘制子view 。

最后调用sWindowSession.finishDrawing() 预计是通知底层完毕吧,不太懂。//TODO

                try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}

draw的部分到此完毕

http://u.cncn.com/space-323-do-blog-id-377295.html

http://u.cncn.com/space-323-do-blog-id-377142.html

http://u.cncn.com/space-mtag-tagid-782.html

http://u.cncn.com/space-mtag-tagid-783.html

至此,一次完整的绘制流程就走完了,剩下的就是一遍遍的反复这个过程啦。

回调过程參见前文《深入分析UI 上层事件处理核心机制 Choreographer

尾巴

尾巴。接下来过一下事件的流程吧。

源代码分析:onAttach, onMeasure, onLayout, onDraw 的顺序。的更多相关文章

  1. 3.View绘制分析笔记之onLayout

    上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout pr ...

  2. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  3. android 自定义view android onmeasure onlayot ondraw

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha android onmeasure onlayot ondraw 顺序 ====== 1 ...

  4. 转:RTMPDump源代码分析

    0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...

  5. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  6. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  7. Raid1源代码分析--开篇总述

    前段时间由于一些事情耽搁了,最近将raid1方面的各流程整理了一遍.网上和书上,能找到关于MD下的raid1的文档资料比较少.决定开始写一个系列的关于raid1的博客,之前写过的一篇读流程也会在之后加 ...

  8. Android 消息处理源代码分析(1)

    Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...

  9. Parrot源代码分析之海贼王

    我们的目的是找到speedup-example在使用Parrot加速的原因,假设仅仅说它源于Context Switch的降低,有点简单了,它究竟为什么降低了?除了Context Switch外是否还 ...

随机推荐

  1. aliyun

    阿里云启动不了网站 1  将网站的目录属性-安全中加入IUSER_计算机名字的访问权限     和  加入NER SERVICE的访问权限 2 IIS打开网站属性--目录--执行权限改为顺脚本 3  ...

  2. Guava源码学习(五)EventBus

    基于版本:Guava 22.0 Wiki:EventBus 0. EventBus简介 提供了发布-订阅模型,可以方便的在EventBus上注册订阅者,发布者可以简单的将事件传递给EventBus,E ...

  3. [jquery] 给动态生成的元素绑定事件 on方法

    用底下的方法尝试了好多次都失败 $('.del').on('click',function(){ alert('aa'); })// 失败!! 终于在准备放弃前看到一篇博文说的方法 $(documen ...

  4. [Usaco2010 Feb]Chocolate Buying

    题目描述     贝西和其他奶牛们都喜欢巧克力,所以约翰准备买一些送给她们.奶牛巧克力专卖店里 有N种巧克力,每种巧克力的数量都是无限多的.每头奶牛只喜欢一种巧克力,调查显示, 有Ci头奶牛喜欢第i种 ...

  5. (转)代码中实现button

    链接地址:http://www.cnblogs.com/hukezhu/p/4500206.html 随着iOS开发发展至今,在UI制作上逐渐分化为了三种主要流派:使用代码手写UI及布局:使用单个xi ...

  6. Linux/Unix面试题

    shell中如何改变文件中的某个关键字 unix命令 unix shell中在特定文件夹内查找包含指定字符串的文件用哪个命令 如何用要shell找到指定目录下的最近一天更新的文件,要包含子目录 Lin ...

  7. python定时执行方法

    1  time.sleep import time for i in range(5): print(i) time.sleep(10) 2 用shed import time import sche ...

  8. remmina rdp远程连接windows

    一.remmina rdp远程连接windows sudo apt-get install remmina 二.ubuntu设置桌面快捷方式 ①找到Remmina远程桌面客户端 比如在[搜索您的本地和 ...

  9. barrier and Fence

    barrier 管理的是commandbuffer里面 command之间 fence管理的是queue之间 queue和cpu之间的顺序 通过flag比如等待所有面片画完 ------------- ...

  10. js获取上传图片的尺寸大小

    当上传图片时,有时候需要控制下上传图片的尺寸大小,需要给个提示 //获取图片的尺寸,控制尺寸大小 var reader = new FileReader(), img = new Image(); / ...