源码版本Android 6.0
请参阅:http://androidxref.com/6.0.1_r10

本文目的是分析从Activity启动到走完绘制流程并显示在界面上的过程,在源码展示阶段为了使跟踪代码逻辑更清晰会省略掉一部分非主干的代码,具体详细代码请翻阅源码。

在上一篇文章Android UI绘制流程分析(二)中我们讲到了Activity的测绘流程是如何开始的,接下来我们开始分析UI绘制的三大流程,本文从measure流程开始:
在上一篇最后讲到performTraversals方法,该方法内部会分别调用

  • performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)
  • performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight)
  • performDraw()

MeasureSpec

MeasureSpec是View类的一个内部类,我们先看看官方文档对MeasureSpec类的描述:

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.

意思其实是 MeasureSpec封装了父布局对子视图的布局要求,由尺寸和模式组成,每个MeasureSpec表示其对子View的宽度或高度要求。

其内部原理是一个32位的int值,高2位表示SpecMode,低30位表示SpecSize。
可以通过makeMeasureSpec来封装一个MeasureSpec,通过getSize()和getMode()来解封获取MeasureSpec内包含的尺寸大小和模式信息。解封操作是通过位运算来获取高2位(getMode())或者低30位(getSize())

#class in View&MeasureSpecpublic static class MeasureSpec {private static final int MODE_SHIFT = 30;private static final int MODE_MASK  = 0x3 << MODE_SHIFT;/*** UNSPECIFIED 模式:* 父View不对子View有任何限制,子View需要多大就多大*/ public static final int UNSPECIFIED = 0 << MODE_SHIFT;/*** EXACTYLY 模式:* 父View已经测量出子Viwe所需要的精确大小,这时候View的最终大小* 就是SpecSize所指定的值。对应于match_parent和精确数值这两种模式*/ public static final int EXACTLY     = 1 << MODE_SHIFT;/*** AT_MOST 模式:* 子View的最终大小是父View指定的SpecSize值,并且子View的大小不能大于这个值,* 即对应wrap_content这种模式*/ public static final int AT_MOST     = 2 << MODE_SHIFT;//将size和mode打包成一个32位的int型数值//高2位表示SpecMode,测量模式,低30位表示SpecSize,某种测量模式下的规格大小public static int makeMeasureSpec(int size, int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}//将32位的MeasureSpec解包,返回SpecMode,测量模式public static int getMode(int measureSpec) {return (measureSpec & MODE_MASK);}//将32位的MeasureSpec解包,返回SpecSize,某种测量模式下的规格大小public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}//...
}

我们在使用View时是直接设置LayoutParams,但是在View测量的时候,系统会将该LayoutParams在结合父布局所给它的MeasureSpec来确定View的测量后的宽高然后生成自己的MeasureSpec,该MeasureSpec保存了View自身的宽高信息同时也包含了对子View的限制规则。

上述会有一个疑问,既然View的测量要根据自身的LayoutParams和父布局的MeasureSpec,那么顶层View(DecorView)哪里来的父布局MeasureSpec?

答:其实DecorView的测量和普通View是有区别的,DecorView的MeasureSpec是由屏幕尺寸和LayoutParams来决定的,并且DecorView的默认LayoutParams是match_parent(在初始化DecorView时可知),而普通View的MeasureSpec是由其LayoutParams和父布局的MeasureSpec决定的。

performTraversals开始测量

#class in ViewRootImplprivate void performTraversals() {...if (!mStopped) {...// 根据屏幕尺寸大小和lp(match_parent)来生成DecorView的MeasureSpecint childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);   int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 1} if (didLayout) {performLayout(lp, desiredWindowWidth, desiredWindowHeight); // 2...}if (!cancelDraw && !newSurface) {if (!skipDraw || mReportNextDraw) {if (mPendingTransitions != null && mPendingTransitions.size() > 0) {for (int i = 0; i < mPendingTransitions.size(); ++i) {mPendingTransitions.get(i).startChangingAnimations();}mPendingTransitions.clear();}performDraw(); // 3}} ...
}private static int getRootMeasureSpec(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;
}

上面说明DecorView的MeasureSpec生成规则是:

LayoutParams MeasureSpec
MATCH_PARENT MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
WRAP_CONTENT MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
else(固定尺寸) MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);

生成之后将结果设置给childHeightMeasureSpec开始执行performMeasure进行测量:

#class in ViewRootImplprivate void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");try {// DecorView开始测量,会走到FrameLayout#measure() -> View#measure()mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}
}
#class in Viewpublic final void measure(int widthMeasureSpec, int heightMeasureSpec) {...if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||widthMeasureSpec != mOldWidthMeasureSpec ||heightMeasureSpec != mOldHeightMeasureSpec) {...int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :mMeasureCache.indexOfKey(key);if (cacheIndex < 0 || sIgnoreMeasureCache) {// 如果没有有效缓存,则重新测量,我们不需要去关心缓存机制,我们要的是跟踪进去测量// measure ourselves, this should set the measured dimension flag backonMeasure(widthMeasureSpec, heightMeasureSpec);...} else {...// 有缓存直接设置缓存结果setMeasuredDimensionRaw((int) (value >> 32), (int) value);mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;}...}...
}

我们从最顶层View(DecorView)为例进行测量分析,具体继续往里测量子View是一个递归过程。
DecorView的真正测量逻辑在FrameLayout#onMeasure(widthMeasureSpec, heightMeasureSpec); 下面代码分析都以FrameLayout为例

#class in FrameLayout@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int count = getChildCount();// 记录FrameLayout布局参数是否是wrap_contentfinal boolean measureMatchParentChildren =MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;mMatchParentChildren.clear();int maxHeight = 0;int maxWidth = 0;int childState = 0;// 遍历测量每一个非GONE的子Viewfor (int i = 0; i < count; i++) {final View child = getChildAt(i);if (mMeasureAllChildren || child.getVisibility() != GONE) {// 将FrameLayout对子View的约束条件(MeasureSpec)传入开始测量子View,这里一会儿展开measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);// 对每一个子View都与本地记录的最大宽高(maxWidth,maxHeight)进行比较,计算出所有子View中需要的最大的宽度和高度。因为假若FrameLayout的LayoutParams属性是wrap_content的话,那FrameLayout的大小取决于子View中最大者final LayoutParams lp = (LayoutParams) child.getLayoutParams();maxWidth = Math.max(maxWidth,child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);maxHeight = Math.max(maxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);childState = combineMeasuredStates(childState, child.getMeasuredState());// 假设FrameLayout的LayoutParams是wrap_content且子View的LayoutParams的宽高中任何一个属性是match_parent的话,就记录下来一会儿会进行重新测量,因为这时子View的大小受FrameLayout的最终大小影响if (measureMatchParentChildren) {if (lp.width == LayoutParams.MATCH_PARENT ||lp.height == LayoutParams.MATCH_PARENT) {mMatchParentChildren.add(child);}}}}...省略前景、背景参与最大宽高计算的代码,这部分很简单,就是当前max与前景、背景取最大值// 保存测量结果,这里一会儿展开setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),resolveSizeAndState(maxHeight, heightMeasureSpec,childState << MEASURED_HEIGHT_STATE_SHIFT));// 注意,这里待会儿分析子View测量的时候会提及到此,先记录一下当个锚点后续好回头看// *****************************************************************count = mMatchParentChildren.size();if (count > 1) {// 当FrameLayout LayoutParams为wrap_content时,对所有LayoutParams为match_parent的子View重新生成一个对子View的布局要求(MeasureSpec)然后进行子View的重新测量for (int i = 0; i < count; i++) {final View child = mMatchParentChildren.get(i);final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();/*** 如果子View的宽度是match_parent属性,那么对当前FrameLayout的MeasureSpec修改:* 把widthMeasureSpec的宽度规格修改为:总宽度 - padding - margin,这样做的意思是:* 对于子Viw来说,如果要match_parent,那么它可以覆盖的范围是FrameLayout的测量宽度* 减去padding和margin后剩下的空间,父View剩多少空间就给你多少空间。** 以下两点的结论,可以查看getChildMeasureSpec()方法:** 如果子View的宽度是一个确定的值,比如50dp,那么FrameLayout的widthMeasureSpec的宽度规格修改为:* SpecSize为子View的宽度,即50dp,SpecMode为EXACTLY模式* * 如果子View的宽度是wrap_content属性,那么FrameLayout的widthMeasureSpec的宽度规格修改为:* SpecSize为FrameLayout的宽度减去padding减去margin,SpecMode为AT_MOST模式*/final int childWidthMeasureSpec;if (lp.width == LayoutParams.MATCH_PARENT) {final int width = Math.max(0, getMeasuredWidth()- getPaddingLeftWithForeground() - getPaddingRightWithForeground()- lp.leftMargin - lp.rightMargin);childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);} else {childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,getPaddingLeftWithForeground() + getPaddingRightWithForeground() +lp.leftMargin + lp.rightMargin,lp.width);}...高度同理//    定好之后进行子View的重新测量,这时重走测量流程,如果子View内又包含子View,则会一层一层往里测量child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}}
}

通过上述代码以及注释可以理解,在onMeasure中总共做了这几件事:

  1. 遍历子View,调用measureChildWithMargins方法对子View进行测量(具体测量规则稍后再说,此处暂时直接认为已经测量完毕)
  2. FrameLayout布局参数为wrap_content时,记录下所有宽或者高布局参数为match_parent的子View到mMatchParentChildren中以便后续进行重新测量
  3. 保存下测量结果,至此FrameLayout的宽高已经测量完毕,宽高已经确定
  4. 取出mMatchParentChildren中的所有View对其重新设置MeasureSpec然后执行测量流程,重设MeasureSpec的规则是(以width为例):
子View LayoutParams MeasureSpec计算规则
match_parent MeasureSpec.makeMeasureSpec(FrameLayout宽度 - padding - margin,MeasureSpec.EXACTLY)
wrap_content MeasureSpec.makeMeasureSpec(FrameLayout宽度 - padding - margin, MeasureSpec.AT_MOST)
固定值(如50dp) MeasureSpec.makeMeasureSpec(50dp, MeasureSpec.EXACTLY)
  1. 使用新生成的MeasureSpec对子View进行重新测量,又是一个遍历的过程

整个流程通了之后,我们来看一下子View具体的测量规则:

#class in ViewGroupprotected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin+ heightUsed, lp.height);// 子View根据测算出来的MeasureSpec已经初步确定了自己的大小,此时如果它也有子View会继续往里进行测量,知道最深层次的View测量完为止child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}// 以宽度测量为例
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;// 父传过来的约束模式switch (specMode) {// Parent has imposed an exact size on us// 当父已经确定其自身宽度时case MeasureSpec.EXACTLY:if (childDimension >= 0) {// 子的宽度为确定值,那么确定子的宽度为childDimension,模式为EXACTLYresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.// 子View宽度为match_parent,此时父是一个精确模式,所以这时子的宽度也可以确定,子宽度为父剩下的可用宽度,模式为EXACTLYresultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.// 子宽度为wrap_content,此时表示子想自己控制宽度,但是父是精确的必须限制子的宽度不许超过父的剩余可用宽度,此时子宽度暂时设置为父的剩余宽度,模式为AT_MOST尽可能大resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on us// 父宽度不确定case MeasureSpec.AT_MOST:// 子的宽度为确定值,那么确定子的宽度为childDimension,模式为EXACTLYif (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us./*** 子想撑满父,但是此时父的宽度并不确定,这是只能先把父剩余的宽度都给它,* 然后把模式设置为AT_MOST,说明它也是个不确定的值,* 一直等到后续子都测量完了才能重新来确定自身宽度,* 真正重新测量的逻辑就是走到FrameLayout咱们之前分析的FrameLayout#onMeasure()星号注释处开始,* 父会重新设置其对子的MeasureSpec然后重新对子进行测量,这时候子View就能得到准确宽度了*/resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.// 当子宽度也是不确定的时候,直接将父的最大可用宽度给它,并且设置其模式为AT_MOST,也会等到后续子都确定了宽度之后进行重新测量resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:...这种模式为系统使用的,我们一般用不到,不做分析break;}// 一系列规则完成之后生成MeasureSpec,此时子View的大小测量暂时告一段落了,返回结果return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

逻辑上感觉很绕,其实梳理之后很好理解,子View的大小根据其自身的LayoutParams和父给它的MeasureSpec来共同决定,大体规则就是遵循:

  1. 父大小精确:
    a.子LayoutParams大小设置准确值,结果就是:MeasureSpec(准确值,EXACTLY)
    b.子想撑满父,则把父剩余的空间全给它,结果就是:MeasureSpec(父SpecSize,EXACTLY)
    c.子也不确定它想自己控制大小,则父只能把最大剩余空间给它,只要它不超出就行,结果就是:MeasureSpec(父SpecSize,AT_MOST)

  2. 父大小不确定:
    a.子LayoutParams大小设置准确值,结果就是:MeasureSpec(准确值,EXACTLY)
    b.子想撑满父,父把能给它的都给它,结果就是:MeasureSpec(父SpecSize,AT_MOST),后面会再测量一次
    c.子想控制自己大小,父也是把能给它的都给它保证它不能超过父剩余的大小,结果就是:MeasureSpec(父SpecSize,AT_MOST)

父大小确定 子LayoutParams大小设置准确值:MeasureSpec(准确值,EXACTLY)
父大小确定 子想撑满父,则把父剩余的空间全给它:MeasureSpec(父SpecSize,EXACTLY))
父大小确定 子想自己控制大小,则父只能把最大剩余大小给它要求子不能超过这个大小:MeasureSpec(父SpecSize,AT_MOST)
父大小不确定 子LayoutParams大小设置准确值:MeasureSpec(准确值,EXACTLY)
父大小不确定 子想撑满父,则把父剩余的空间全给它:MeasureSpec(父SpecSize,AT_MOST),后续会重新测量确定真正的大小
父大小不确定 子想自己控制大小,则父只能把最大剩余大小给它要求子不能超过这个大小:MeasureSpec(父SpecSize,AT_MOST),也会重新测量确定真正大小

参考网上的一张图片,过程就是:
接着看下测量完成之后需要将测量的结果设置给mMeasuredWidth和mMeasuredHeight,后续我们使用的时候调用View.getMeasureWidth()等方法时才能拿到具体的值。每个View在测量完成之后是通过 setMeasuredDimension(int measuredWidth, int measuredHeight)方法来将结果保存给View的。

#class in Viewprotected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {...setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {mMeasuredWidth = measuredWidth;mMeasuredHeight = measuredHeight;mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}

至此测量流程已经结束

真正当我们自定义View时如果需要涉及到View的测量流程,在测量的最后必须将结果通过setMeasuredDimensionRaw设置给View,这样才是一个完整的自定义View的测量流程,否则你所测量出来的结果是不生效的。
大致可以这么做:


//假设自定义一个View宽度为10,高度为20
public class CustomView extends View {public CustomView(Context context) {super(context);}// 方法1 直接继承onMeasure,然后生成新的MeasureSpec传给父@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int newWidthSpec = MeasureSpec.makeMeasureSpec(10, MeasureSpec.AT_MOST);int newHeightSpec = MeasureSpec.makeMeasureSpec(20, MeasureSpec.AT_MOST);super.onMeasure(newWidthSpec, newHeightSpec);}// 方法2 确定好宽高之后直接设置给View@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(10, 20);}
}

Android UI绘制流程分析(三)measure相关推荐

  1. 【Android 应用开发】UI绘制流程 ( 生命周期机制 | 布局加载机制 | UI 绘制流程 | 布局测量 | 布局摆放 | 组件绘制 | 瀑布流布局案例 )

    文章目录 一. 博客相关资料 及 下载地址 1. 代码查看方法 ( ① 直接获取代码 | ② JAR 包替换 ) 2. 本博客涉及到的源码查看说明 二. Activity 生命周期回调机制 1. An ...

  2. Android SDCard UnMounted 流程分析(三)

    前篇地址 Android SDCard UnMounted 流程分析(一) Android SDCard UnMounted 流程分析(二) 前一篇讲到SDCard unmout onEvent 发送 ...

  3. Android源码解析:UI绘制流程之控件绘制

    带着问题看源码 再接再厉,我们来分析UI绘制流程最后一步绘制流程 入口ViewRootImpl.performDraw()方法 private void performDraw() {//...try ...

  4. Android视图绘制流程完全解析,带你一步步深入了解

    Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了解View(二) Android视图状态及重绘流程分析,带你 ...

  5. Android RecyclerView 绘制流程及Recycler缓存

    前言 RecyclerView源码一万多行,想全部读懂学会挺麻烦的,感兴趣的可以自己去瞅瞅,这篇文章重点来看下 RecyclerView是如何一步步将每一个 ItemView 显示到屏幕上,然后再分析 ...

  6. android camera2 API流程分析

    Android camera2 API流程分析 Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原 ...

  7. Android -- Wifi启动流程分析

    Android -- Wifi启动流程分析 Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了:下面就简单介绍下Android中Wifi的启动流程. 当我在Setting菜单里点击 ...

  8. 【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)

    [SemiDrive源码分析][X9芯片启动流程]27 - AP1 Android Preloader启动流程分析(加载atf.tos.bootloader镜像后进入BL31环境) 一.Android ...

  9. React Native UI渲染流程分析(Android)

    前言 React Native App(后称RN App)的UI由JS端的View tree构成,在App运行时会创建相应的原生View tree.从结果看,这和安卓原生开发时用xml布局文件是一样的 ...

最新文章

  1. [转]CS的顶级会议和期刊
  2. Dubbo 源码分析 - 自适应拓展原理
  3. 前端之JavaScript第一天学习(1)-JavaScript 简介
  4. 云炬Android开发笔记 2-3Android Studio如何导入Github上的项目
  5. secureCRT连接服务器和文件传输( 一步搞定)
  6. 元学习Meta Learning/Learning to learn
  7. linux套接字端口,多端口监听套接字linux(multiport listening socket linux)
  8. 某“高人”谈论股市,对现在行情的分析
  9. 有关风向及风向处理的笔记
  10. 《UNIX编程艺术》--读书笔记
  11. Linux编程:获取时间戳
  12. 【建模应用】PLS偏最小二乘回归原理与应用
  13. Windows API 获取所有进程音量并调节
  14. c语言补偿算法,C功能刀具半径补偿算法与实现.doc
  15. 风火编程--opencv使用记事
  16. HTTP 长连接短连接使用场景是什么
  17. 什么是SQL注入?怎么解决SQL注入?
  18. 计算机音乐播放器设置,Win7系统下设置默认音乐播放器的两种方法
  19. EASY EAI Nano人工智能开发套件免费试用啦!
  20. Python3基本用法 2020.2.20

热门文章

  1. wpf修改鼠标悬停效果
  2. 电影投资普通小白参与能获得多少分红?分红有规则吗?
  3. 对话以太坊研究员 Polynya:为什么 L2 是未来?
  4. 【Kotlin】Android-使用WebDAV协议云存储文件(详细)—附demo源码
  5. android应用是非正式版本,华为手机自带浏览器,应用是非正式发布版本,当前设备不支持安装
  6. python实现批量注册网站用户
  7. 雷达系列论文翻译(六):LIO-SAM
  8. java从小白到老白②
  9. Netty5基础知识介绍及简单使用
  10. 增量式旋转编码器工作原理