节点信息查询 API 可以用于获取节点属性、样式、在界面上的位置等信息。最常见的用法是使用这个接口来查询某个节点的当前位置,以及界面的滚动位置。如下图所示,里面有我们所需要的height,我们将这个height赋值给swiper组件,再令image标签mode="widthFix",即可自动适应轮播图高度和图片的高度保持一致
classClassInterceptor() :ContinuationInterceptor {override val key = ContinuationInterceptor override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =MyContinuation(continuation)
} class MyContinuation<T>(private val continuation: Continuation<T>):Continuation<T> by continuation{ override fun resumeWith(result: Result<T>) { Log.e("hello","MyContinuation start ${result.getOrThrow()}") continuation.resumeWith(result)
class ClassInterceptor() : ContinuationInterceptor { override val key = ContinuationInterceptor override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = MyContinuation(continuation)
}
class MyContinuation<T>(private val continuation: Continuation<T>) : Continuation<T> by continuation { private val handler = Handler(Looper.getMainLooper()) override fun resumeWith(result: Result<T>) { Log.e("hello", "MyContinuation start ${result.getOrThrow()}")
handler.post { continuation.resumeWith(Result.success(自定义内容)) } Log.e("hello", "MyContinuation end ") } }
同时把async默认参数CoroutineContext实现一下即可
fun async( context: CoroutineContext = ClassInterceptor(), block: suspend AsyncScope.() -> Unit ) { // 这个有两个作用 1.充当receiver 2.completion,接收回调 val completion = AsyncStub(context) block.startCoroutine(completion, completion) }
此后我们就可以直接通过,完美实现了一个类js协程的调用,同时具备了自动切换线程的能力
async { val result = await { test() } Log.e("hello", "result is $result ${Looper.myLooper() == Looper.getMainLooper()}") }
结果
E start E MyContinuation start kotlin.Unit E MyContinuation end E end E 执行阻塞函数 test 1923 E MyContinuation start 自定义内容数值 E MyContinuation end E result is 自定义内容的数值 true E AsyncStub resumeWith 2 kotlin.Unit
class ClassInterceptor() : ContinuationInterceptor { override val key = ContinuationInterceptor override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = MyContinuation(continuation)
}
class MyContinuation<T>(private val continuation: Continuation<T>) : Continuation<T> by continuation { private val handler = Handler(Looper.getMainLooper()) override fun resumeWith(result: Result<T>) { Log.e("hello", "MyContinuation start ${result.getOrThrow()}")
handler.post { continuation.resumeWith(Result.success(6 as T)) } Log.e("hello", "MyContinuation end ") } }
interface AsyncScope { fun myFunc(){
}
} fun async( context: CoroutineContext = ClassInterceptor(), block: suspend AsyncScope.() -> Unit ) { // 这个有两个作用 1.充当receiver 2.completion,接收回调 val completion = AsyncStub(context) block.startCoroutine(completion, completion) }
class AsyncStub(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation<Unit>, AsyncScope { override fun resumeWith(result: Result<Unit>) {
泛型参数会在编译期间存在,在运行期间会被擦除,例如:Generics<EnglishBook> 与 Generics<MathBook> 的实例都会被擦除为 Generics<*>。运行时期检测一个泛型类型的实例无法通过is关键字进行判断,另外运行期间具体的泛型类型判断也无法判断,如: books as List<Book>,只会对非泛型部分进行检测,形如:books as List<*>。
如果想具体化泛型参数,可以通过inline + reified的方式:
/** * inline + reified 使得类型参数被实化 reified:实体化的 * 注:带reified类型参数的内联函数,Java是无法直接调用的 */ inline fun <reified T> isAny(value: Any): Boolean { return value is T }
TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,即在收发数据钱,都需要与对面建立可靠的链接,这也是面试经常会问到的TCP的三次握手以及TCP的四次挥手!三次握手:建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立,在Socket编程中,这一过程由客户端执行connect来触发,具体流程图如下:
当 CPU 运行于用户空间(执行用户空间的指令)时,它处于用户态,只能执行普通的 CPU 指令 ,当 CPU 运行于内核空间(执行内核空间的指令)时,它处于内核态,可以执行清内存,置时钟,读写文件等特权指令,那怎么区分 CPU 是在用户态还是内核态呢,CPU 定义了四个特权等级,如下,从 0 到 3,特权等级依次递减,当特权级为 0 时,CPU 处于内核态,可以执行任何指令,当特权级为 3 时,CPU 处于用户态,在 Linux 中只用了 Ring 0,Ring 3 两个特权等级
我们注意到现在的做法是一次性为进程分配了占用其所有虚拟空间的页表项,但实际上一个进程根本用不到这么巨大的虚拟空间,所以这种分配方式无疑导致很多分配的页表白白浪费了,那该怎么办,答案是分级管理,等真正需要分配物理空间的时候再分配,其实大家可以想想我们熟悉的 windows 是怎么分配的,是不是一开始只分配了 C 盘,D盘,E盘,等要存储的时候,先确定是哪个盘,再在这个盘下分配目录,然后再把文件存到这个目录下,并不会一开始就把所有盘的空间给分配完的
既然分页这么好,那么分段是不是可以去掉了呢,理论上确实可以,但 Intel 的 CPU 严格执行了 backward compatibility(回溯兼容),也就是说最新的 CPU 永远可以运行针对早期 CPU 开发的程序,否则早期的程序就得针对新 CPU 架构重新开发了(早期程序针对的是 CPU 的段式管理进行开发),这无论对用户还是开发者都是不能接受的(别忘了安腾死亡的一大原因就是由于不兼容之前版本的指令),兼容性虽然意味着每款新的 CPU 都得兼容老的指令,所背的历史包袱越来越重,但对程序来说能运行肯定比重新开发好,所以既然早期的 CPU 支持段,那么自从 80386 开始的所有 CPU 也都得支持段,而分页反而是可选的,也就意味着这些 CPU 的内存管理都是段页式管理,逻辑地址要先经过段式管理单元转成线性地址(也称虚拟地址),然后再经过页式管理单元转成物理内存,如下
在 Linux 中,虽然也是段页式内存管理,但它统一把 CS,DS,SS,ES 的段基址设置为了 0,段界限也设置为了整个虚拟内存的长度,所有段都分布在同一个地址空间,这种内存模式也叫平坦内存模型(flat memory model)
我们知道逻辑地址由段选择子:段内偏移地址组成,既然段选择子指向的段基地址为 0,那也就意味着段内偏移地址即为即为线性地址(也就是虚拟地址),由此可知 Linux 中所有程序的代码都使用了虚拟地址,通过这种方式巧妙地绕开了分段管理,分段只起到了访问控制和权限的作用(别忘了各种权限检查依赖 DPL,RPL 等特权字段,特权极转移也依赖于段选择子中的 DPL 来切换的)
创建Activity对象,最终instantiateActivity()通过调用Class的newInstance()方法,反射创建出来,方法注解到This method is only intended to provide a hook for instantiation. It does not provide earlier access to the Activity object. The returned object will not be initialized as a Context yet and should not be used to interact with other android APIs.,方法只创建了Activity的早期对象,并没有对它做Context的初始化,所以不能调用安卓相关api。简单来说,Activity本身继承自ContextWrapper,这个方法并没有具体实现任何Context的方法,只是将所有方法代理给了内部的baseContext,所以反射创建后,调用任何的系统的方法都是无效的。
这个项目就是一个示例,并不是一个需要固守不可改变固定结构,相反而是可以根据需求就行变化的。根据 Now in Android 这是我们发现最适合我们项目的一般准则,并提供了一个示例,可以在此基础上进一步修改、扩展和构建。如果您的数据层很小,则可以将其保存在单个模块中。但是一旦存储库和数据源的数量开始增长,可能值得考虑将它们拆分为单独的模块。
private Choreographer(Looper looper, int vsyncSource) {
//创建Looper对象
mLooper = looper;
//接受处理消息
mHandler = new FrameHandler(looper);
//用来接受垂直同步脉冲,也就是Vsync信号
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
//计算下一帧的时间,Androoid手机屏幕是60Hz的刷新频率
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//初始化CallbackQueue,将在下一帧开始渲染时回调
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
主要来看下FrameHandler和FrameDisplayEventReceiver的数据结构:
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//开始渲染下一帧的操作
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
//请求Vsync信号
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
//请求执行Callback
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
在FrameHandler可以看到对三种消息进行了处理,对其具体实现一会分析。
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
}
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
......
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
到了这一步看到还是会调用到scheduleFrameLocked方法。
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
//开启了Vsync
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
if (isRunningOnLooperThreadLocked()) {
//申请Vsync信号
scheduleVsyncLocked();
} else {
//最终还是会调用到scheduleVsyncLocked方法
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
//如果没有直接使用Vsync的话,则直接通过该消息执行doFrame
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
//申请VSYNC信号,会回调onVsunc方法
nativeScheduleVsync(mReceiverPtr);
}
}
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
......
//设置当前frame的Vsync信号到来时间
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
//时间差大于一个时钟周期,认为跳frame
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//跳frame数大于默认值,打印警告信息,默认值为30
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
//计算实际开始当前frame与时钟信号的偏差值
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
//若时间回溯,则不进行任何工作,等待下一个时钟信号的到来
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
//请求下一次时钟信号
scheduleVsyncLocked();
return;
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
......
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
//迭代执行所有队列任务
for (CallbackRecord c = callbacks; c != null; c = c.next) {
.....
//调用CallbackRecord内的run方法
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
主要是去遍历CallbackRecrd,执行所有任务:
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
@UnsupportedAppUsage
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
fun runTask(task: () -> Any) {
when (val result = task()) {
Unit -> println("result is Unit")
String -> println("result is a String: $result")
else -> println("result is an unknown type")
}
}
publicvoidpostSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
} // Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
postSticky 代码比较简单,首先对 stickyEvents 进行加锁,接下来把 event 事件的 Class 对象作为 Key,event 事件本身作为 value 放进 Map 中,其中stickyEvents 是 Map 对象,实例是 ConcurrentHashMap, 其 Key 和 Value 的泛型形参分别是 Class 和 Object, 它的作用就是用来存储粘性事件;然后调用 post(event) 把粘性事件当作普通事件发送一下。
首先我们看下最后为什么要调用下 post(event)?
虽然 post(evnet) 上面有注释,简单翻译下:"在放进 Map 后应该再发送一次,以防止订阅者想立即删除此事件",读完注释后,可能还是不太明白,这里笔者认为:在前面存储完粘性事件后,这里调用 post 把粘性事件当作普通事件发送出去,或许是因为现在已经有注册的粘性事件订阅者,此时把已经注册的粘性事件订阅者当作普通事件的订阅者,这样已经注册的粘性事件订阅者可以立即收到相应的事件,只是此时事件不再是粘性的。
privatevoid checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { if (stickyEvent != null) { // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
可能细心的读者已经发现 test 方法调用了,问题应该出在 postSticky 方法中,让我们再次查看 postSticky 方法:
private final Map, Object> stickyEvents;
publicvoidpostSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
} // Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
根据前面分析 postSticky 方法的结果,stickyEvents 用于存储粘性事件,它是个 Map 结构,而 stickyEvents 的 Key 正是 Event 的 Class 对象,根据 Map 结构的存储原理:如果存在相同的 Key,则覆盖 Value 的值,而 stickyEvents 的 Value 正是 Event 本身。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD})
public @interface Subscribe { ThreadModethreadMode() defaultThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/ booleansticky() defaultfalse;
// 增加消息必达的方法 booleanrendezvous() defaultfalse;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */ intpriority() default0;
}
// 如果订阅者和订阅者父类中没有Event接收方法则抛出异常 if (subscriberMethods.isEmpty()) { thrownew EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else { // 添加进缓存中
METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods;
}
}
privatevoidfindUsingReflectionInSingleClass(FindState findState){
Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities // 通过反射获取当前类中声明的所有方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) { // 删减不关心的代码
}
// 遍历所有方法 for (Method method : methods) {
// 获取方法的修饰符 int modifiers = method.getModifiers();
// 选择List存储必达事件,使用Pair封装必达事件的Key和Value private final List, Object>> rendezvousEvents;
publicvoidpostRendezvous(Object event) {
synchronized (rendezvousEvents) {
rendezvousEvents.add(Pair.create(event.getClass(), event));
} // Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
上面的源码,我们通过仿照 postSticky 方法实现了 postRendezvous 方法,在 postSticky 方法中使用 Map 存储粘性事件,不过我们在 postRendezvous 方法中使用 List 存储必达事件,保证必达事件不会因为 Key 相同而被覆盖丢失,最后也是调用 post 方法尝试先发送一次必达事件。
综艺平时也是我解压的一种方式,最近把跑男第十季追完了,几位 mc 都是各有特点。不过最喜欢的还是新加入的白鹿,人美,很搞笑,魔性的笑声让人很容易记住她。
你问我:我对你有多重要,我回答:太阳你知道吧
总结
可能有人看了之后会觉得有点躺平的趋势,但其实并没有。本人还是很爱折腾的,也希望能多认识点圈子以外的人,多认识点有趣的人,多认识点志同道合的人。有些人会觉得程序员很闷,不爱说话,天天就对着电脑。可能有部分人是这样的,但我不是,因为我是一个不走寻常路的程序员,而且我深知只有跳出圈子,才能打破认知。by the way,本人对数字化转型行业挺感兴趣的,有读者从事或者了解的话,可以大胆私信我啊。