dependencies { //grpc dependencies // You need to build grpc-java to obtain these libraries below. implementation 'io.grpc:grpc-okhttp:1.44.1'// CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' implementation 'io.grpc:grpc-protobuf-lite:1.45.0' implementation 'io.grpc:grpc-stub:1.45.0' }
protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { finalMarginLayoutParamslp= (MarginLayoutParams) child.getLayoutParams();
java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6899) at com.hezd.taglayout.FlowLayout.onMeasure(FlowLayout.kt:23) at android.view.View.measure(View.java:24851) at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811)
protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { // 子ViewLayoutParams强转为MarginLayoutParms finalMarginLayoutParamslp= (MarginLayoutParams) child.getLayoutParams();
/** * *@author hezd *Create on 2021/12/24 17:08 */ classFlowLayout(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs) { var lineSpacing: Int var itemSpacing: Int
privatevoidperformTraversals() { // 忽略部分代码…… intchildWidthMeasureSpec= getRootMeasureSpec(mWidth, lp.width); intchildHeightMeasureSpec= getRootMeasureSpec(mHeight, lp.height); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); }
privatestaticintgetRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
privatestaticintgetRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
publicstaticintgetChildMeasureSpec(int spec, int padding, int childDimension) { // 父view的MeasureSpec约束 intspecMode= MeasureSpec.getMode(spec); intspecSize= MeasureSpec.getSize(spec);
intsize= Math.max(0, specSize - padding);
intresultSize=0; intresultMode=0; // 通过父View的MeasureSpec结合子View的布局信息 // 计算出子View的MeasureSpec约束 switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
// Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
// Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
// Lay out child directly against the parent measure spec so that // we can obtain exected minimum width and height. // 如果父View传递的specMode约束为UNSPECIFIED测量第一个条目高度childHeight measureScrapChild(child, 0, widthMeasureSpec, heightSize);
@Override protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { finalMarginLayoutParamslp= (MarginLayoutParams) child.getLayoutParams();
/** * This is called to find out how big a view should be. The parent * supplies constraint information in the width and height parameters. * 从文档注释可以看出:widthMeasureSpec和heightMeasureSpec是父View对子View尺寸测量的一个约束信息 */ publicfinalvoidmeasure(int widthMeasureSpec, int heightMeasureSpec) { //忽略部分代码... if (forceLayout || needsLayout) { // 忽略部分代码... intcacheIndex= forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } } }
/** * View的onLayout是空实现,ViewGroup需要重写此方法 * 对子View进行布局 * Called from layout when this view should * assign a size and position to each of its children. * */ protectedvoidonLayout(boolean changed, int left, int top, int right, int bottom) { }
/** * A MeasureSpec encapsulates the layout requirements passed from parent to child. * Each MeasureSpec represents a requirement for either the width or the height. * A MeasureSpec is comprised of a size and a mode... */ publicstaticclassMeasureSpec
publicstaticintgetChildMeasureSpec(int spec, int padding, int childDimension) { // 父View的MeasureSpec intspecMode= MeasureSpec.getMode(spec); intspecSize= MeasureSpec.getSize(spec); // 可用空间 intsize= Math.max(0, specSize - padding);
intresultSize=0; intresultMode=0;
switch (specMode) { // 如果父View的specMode是EXACTLY case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
// 如果父View的specMode是AT_MOST case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
// 如果父View的specMode是UNSPECIFIED case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
booleanenqueueMessage(Message msg, long when) { //...忽略部分代码
synchronized (this) { //... 忽略部分代码
msg.markInUse(); msg.when = when; Messagep= mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; }
// We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } returntrue; }
publicstaticvoidprepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { thrownewIllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
prepareMainLooper又调用了prepare方法
privatestaticvoidprepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { thrownewRuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(newLooper(quitAllowed)); }
Looper死循环为什么不会导致anr App本质上也是一个java程序,入口就是ActivityThread的main方法,如果main方法执行完程序就退出了,如何保证不退出就是写一个死循环,ActivityThread中初始化了Looper并调用了loop在loop方法中开启了一个死循环阻塞了主线程这样程序可以保证程序一直执行不会退出。几乎所有的GUI程序都是这么实现的。既然是死循环那么其他代码怎么运行,页面交互怎么处理呢?Android是基于事件驱动的,不管是页面刷新还是交互本质上都是事件,都会被封装成Message发送到MessageQueue由Looper进行分发处理的。ANR是什么,Application no responding应用无响应,为什么没响应,因为主线程做了好事操作,loop方法死循环也会阻塞主线程为什么不会anr,什么是响应,响应就是页面刷新,交互处理等,谁来响应,其实就是looper的loop方法,,主线程做了耗时操作会阻塞loop方法导致无法处理其他message所以导致anr。
Linux的epoll机制 我们先来看下Message的next方法
Message next() { //...省略部分代码 for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //重点关注这一行 nativePollOnce(ptr, nextPollTimeoutMillis);