最新要闻
- 小事记 | 龙光清盘呈请聆讯押后 华夏幸福下属公司列为失信被执行人_播资讯
- 上映1天票房破亿!美国大片《速度与激情10》豆瓣7.0分:巨石强森惊喜彩蛋|快看
- 年轻人第一辆插混SUV来了!深蓝S7将于5月20日开订:20万内最帅最能打?
- 世界报道:解决《王国之泪》掉帧严重 Switch超频教程来了 稳定30帧运行
- 华为、小米、OPPO、vivo联手:快充终于要统一了?!
- 男子暴饮暴食后血液滤出400毫升油脂:甘油三酯严重超标|每日精选
- 是故学然后知不足断句_是故学然后知不足
- 整式的乘除计算题及答案_整式的乘除计算题
- 网友模仿《漫长的季节》秦昊食谱 导致将自己送进医院引发热议
- 任天堂Switch平台《塞尔达传说:王国之泪》上市三天取得好成绩 销量破千万
- 索尼将于5月25日举行新一期游戏发布会 市场高达一个小时
- 特斯拉考虑在印度建立一家汽车制造工厂 未讨论降低电动汽车进口关税
- Steam Deck掌机因振动反馈技术惹上麻烦 Valve遭到起诉
- 《王者荣耀》蝉联2023年4月全球手游畅销榜冠军 新角色姬小满登场
- 演员许娇晒出泳装照引发网友热议 正面回复称大大方方展示
- 电影《变形金刚7》发布全新海报 中国内地正式定档6月9日上映
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
我所知道的Handler
简单讲,handler就是两个功能
【资料图】
插入消息,enqueuemessage,msg,when从消息队列中遍历所有消息,比对msg.when和当前的when,找到合适的位置插入
处理消息,looper.loop会从messagequeue中调用next。取消息,如果消息还没到时间该执行,就会比对时间,下次轮询就通过binder写入,native函数休眠,到时间唤醒执行。
handler内存泄漏
GCRoot 一般是静态变量或者常量可以作为GCROOTGCROOT 是ThreadLocal,存在于Looper中,Looper被加载就存在,handler持有activity或者fragment,handler又被message持有,message的target属性,message被messagequeue持有,messagequeue被looper中的threadlocal持有
java中匿名内部类会默认持有外部类的引用
打断持有链
- handler.removemesage handler.removecallbacks
- handler使用static修饰。
主线程的Looper不允许退出
处理消息,looper取出来后,调用message.tager.dispatchemesage后面调用了handler的handlemessage方法。
还有个callback对象,如果有callback,dispatch会先执行callback的处理,calllback返回true,后面就不处理了,callback返回false就给handler的handlemessage处理了
Meesage对象创建
message创建用的obtain,池化,频繁的创建销毁会导致内存不稳定,抖动,造成卡顿 oom等问题
message pool的最大缓存50
阻塞和休眠,阻塞是被动的,休眠是主动的,阻塞不会让出cpu,休眠会,thread.yield会让出cpu。
子线程主线程通信
handler,livedata,eventbus,flow,rxjava,broadcast,观察者模式不能跨线程
最终都是handler完成的。
Handler监听卡顿
原理是在looper内完成的,looper处理消息的时候,会打印内容,就是Printer,looper可以设置它。
Message消息的分类
同步消息,普通的消息都是同步消息异步消息,创建handler的时候设置async为true即可同步屏障,需要通过反射调用,app层无法直接调用,是messagequeue提供的posSyncBarrier方法实现的,返回一个token,时msg的arg1值,用它来取消同步屏障。和普通消息的区别是,msg。target属性为null。
刷新UI的消息是异步消息,发送前先插入了一个同步屏障消息,异步消息处理完成后,要将同步屏障消息移除队列
消息入队
handler消息加入队列,有一系列方法,如下:
// 发送空消息public final boolean sendEmptyMessage(int what){}public final boolean sendEmptyMessageDelayed(int what,long delay){}public final boolean sendEmptyMessageAtTime(int what,long when){}// 发送消息public final boolean sendMessage(@NonNull Message msg){}public final boolean sendMessageDelayed(@NonNull Message msg,long time){}public final boolean sendMessageAtTime(@NonNull Message msg,long when){}public final boolean sendMessageAtFrontOfQueue(Message msg) {}// post发送public final boolean post(@NonNull Runnable r) {}public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {}public final boolean postAtTime( @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {}public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {}public final boolean postDelayed(Runnable r, int what, long delayMillis) {}public final boolean postDelayed( @NonNull Runnable r, @Nullable Object token, long delayMillis) {}public final boolean postAtFrontOfQueue(@NonNull Runnable r) {}// enqueuepublic final boolean executeOrSendMessage(@NonNull Message msg) {}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {}
最终都是掉用的enqueueMessage加入队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; // 绑定消息处理对象 msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { // 根据handler创建是否是异步的,来将消息标记为异步消息 msg.setAsynchronous(true); } // 调用messagequeue的方法,加入队列。 return queue.enqueueMessage(msg, uptimeMillis); }
下面看下消息如何入队的
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; // 当前队列的头 Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. // 消息队列为null,或者消息是立刻执行的,或者要执行的时间先于头消息的时间,将当前消息作为新的队列的头 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; } // 如果需要的话,唤醒队列,开始处理消息,就是MessageQueue的next方法开始执行 // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
取消息以及消息处理
取消息1
取消息入口是Looper的loop方法处理的
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn"t called on this thread."); } if (me.mInLoop) { Slog.w(TAG, "Loop again would have the queued messages be executed" + " before this one completed."); } me.mInLoop = true; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell "setprop log.looper.1000.main.slow 1 && stop && start" final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); me.mSlowDeliveryDetected = false; // 无限循环,通过loopOnce取消息 for (;;) { if (!loopOnce(me, ident, thresholdOverride)) { return; } } }
处理消息
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { // 取消息 Message msg = me.mQueue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return false; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // Make sure the observer won"t change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { // 这里取出msg的target属性,就是handler对象,进行消息的处理。注意:屏障消息是没有target的。 msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (me.mSlowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); me.mSlowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. me.mSlowDeliveryDetected = true; } } } // 这里会打印那些执行慢的消息 if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn"t corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); return true; }
取消息2
再看下实际的取消息的方法,MessageQueue的next方法
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } // 这个方法会在没有消息的时候阻塞 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; // 消息头保存 Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. // 这里处理同步屏障消息,如果当前消息是异步消息,就跳出循环,否则继续循环 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); // 循环的作用,找出队列里的异步消息,存储在msg里,或者将同步屏障消息存储在msg中,prevMsg存储的是同步消息 } if (msg != null) { if (now < msg.when) { // 下次的唤醒时间 // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); // 将取出的消息交给handler处理。 return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
handler的消息处理
是从dispatchMessage方法开始的,里面进行消息处理
public void dispatchMessage(@NonNull Message msg) {// 检查message是否设置了callback对象(runnable类型的)if (msg.callback != null) {// 执行其run方法handleCallback(msg);} else {// 检查handler是否设置了callback对象if (mCallback != null) {if (mCallback.handleMessage(msg)) {// 如果返回true,后续就不执行了。return;}}// 自定义handler的时候,实现该方法处理消息handleMessage(msg);}}
IdleHandler是什么?干什么?继续看MessageQueue类。
IdleHandler是MessageQueue的静态内部接口。如下
public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle(); }
当前线程的消息对立内当前没有消息要处理时,会取出idlehander执行任务,因为执行在主线程,禁止执行耗时操作。返回true表示执行完并不会移除该对象,false执行完一次就移除。而且,执行时机是不确定的。执行的地方在next方法内部。
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // 执行到这里,说明没有找到message对象需要执行了,且线程没有退出。 // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } // 取出idlehandlers if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { // 执行idlehandler的方法 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { // 需要移除的话,在此处进行移除 synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
关键词:
我所知道的Handler
百事通!解决xorm逆向工程问题
小事记 | 龙光清盘呈请聆讯押后 华夏幸福下属公司列为失信被执行人_播资讯
上映1天票房破亿!美国大片《速度与激情10》豆瓣7.0分:巨石强森惊喜彩蛋|快看
年轻人第一辆插混SUV来了!深蓝S7将于5月20日开订:20万内最帅最能打?
世界报道:解决《王国之泪》掉帧严重 Switch超频教程来了 稳定30帧运行
华为、小米、OPPO、vivo联手:快充终于要统一了?!
男子暴饮暴食后血液滤出400毫升油脂:甘油三酯严重超标|每日精选
是故学然后知不足断句_是故学然后知不足
整式的乘除计算题及答案_整式的乘除计算题
网友模仿《漫长的季节》秦昊食谱 导致将自己送进医院引发热议
任天堂Switch平台《塞尔达传说:王国之泪》上市三天取得好成绩 销量破千万
索尼将于5月25日举行新一期游戏发布会 市场高达一个小时
特斯拉考虑在印度建立一家汽车制造工厂 未讨论降低电动汽车进口关税
Steam Deck掌机因振动反馈技术惹上麻烦 Valve遭到起诉
《王者荣耀》蝉联2023年4月全球手游畅销榜冠军 新角色姬小满登场
演员许娇晒出泳装照引发网友热议 正面回复称大大方方展示
电影《变形金刚7》发布全新海报 中国内地正式定档6月9日上映
日本最大网络广告代理商推出日语最大级别AI语言模型 可商业使用
童年回忆《知音漫客》将于今年5月至10月之间休刊 曾连载众多知名作品
世界观天下!管理好一个团队的方法(这样管理好一个团队)
讯息:首日票房破1亿!《速度与激情10》开分9.4 豆瓣首批口碑出炉
华为MateBook E 2023款来袭:重新定义二合一笔记本
雷军喝了一杯FENDI联名喜茶:杯子跟小米13绝配 环球热推荐
官方回应兵马俑发掘还要700年:不是那样的
完美兼容安卓!苹果发布Beats Studio Buds+真无线蓝牙耳机:降噪提升1.6倍 环球观热点
京东太猛,手写hashmap又一次重现江湖-全球今日报
【世界热闻】SpringBoot+MyBatis+MySQL电脑商城项目实战(四)用户注册—控制层
自定义一个简单的Task调度器、任务循环调度器、TaskScheduler
货币市场日报:5月17日
世界要闻:苹果WWDC来了!iOS 17有三大变化
新消息丨葡萄健康栽培与病虫害防控(关于葡萄健康栽培与病虫害防控的简介)
每日快看:318川藏线巨石滚落砸烂一轿车:车内人员躲过一劫
荣耀90 Pro真机曝光:“星钻银”配色耀眼 灵感来自珠宝王冠-环球快看点
马斯克称特斯拉电动皮卡将在今年开始交付 不打算卸任CEO-最新资讯
比用毛巾还便宜 不怕有味儿:大牌洗脸巾7.9元100抽狂促
美国教授用ChatGPT判定学生论文抄袭:结果尴尬!聪明反被聪明误|今日热门
天天快资讯:时隔3年再“发车”,“北斗专列”如何升级?
【Linux】详解Centos7的下载安装配置
焦点日报:【财经分析】多重因素影响REITs市场表现 短期调整无碍机构长期看多
概念动态|机器人新增“比亚迪概念”
世界球精选!你会买吗?澳航推“邻座无人”服务 最低仅140元
十年果粉换OPPO Find X6 Pro后直呼惊艳:果断把iPhone 14 Pro挂闲鱼卖掉|世界聚看点
视讯!拳头性别歧视案尘埃落定 将赔偿每位女性最多15万刀
男子为稳坐榜一大哥骗取乙方百万:全部打赏给女主播 被判刑
世界报道:巴西貘被饲养员挠痒一脸舒适 网友:长得东拼西凑但依然很萌
上海市首届中青年工程师创新创业大赛启动
债市日报:5月17日
每日机构分析:5月17日 环球焦点
小米发布米家空调巨省电2匹:新一级能效 一年省380度电
【天天速看料】孟羽童接私活被开除 专家称老板不能要求员工没副业
全球微头条丨PC已死?联想不同意!
重新打趴中国厂商 韩国不服输:显示面板要夺回第一|天天时快讯
最后的低价?2TB三星980 Pro最低959到手
斗罗大陆冰凤凰vs火凤凰(斗罗大陆冰蝶舞)
springCloud Alibaba服务的注册与发现之eureka客户端注册-每日快看
最佳软件测试基础入门教程3软件开发生命周期的测试
黄牛为什么能抢走“五月天”的门票?|当前速讯
天天关注:记录--vue3优雅的使用element-plus的dialog
上映首日!《速度与激情10》票房突破8000万-全球看点
消息称58同城开启大裁员:裁撤比例50%以上-全球快讯
过去一年 腾讯员工减少1万人 平均工资又涨了 世界热推荐
国产龙芯3B600八个大小核、自研GPU:性能媲美Zen2、10代酷睿-当前播报
世界微动态丨你看微信视频号么?腾讯:粉丝破万创作者是去年三倍
天天视点!欧洲“核联盟”为欧洲核能发展拟定“路线图”
软件测试精品书籍文档下载
热议:4大特性看Huawei Cloud EulerOS为开发者带来平滑迁移体验
设计软件的二次开发总结(表格)
环球热门:金智科技接待中泰证券等多家机构调研
【时快讯】乌兹别克斯坦官员:乌兹别克斯坦愿与中国展开全方位各领域合作
被要求停服!CyGames《赛马娘》遭专利起诉:国服B站代理-每日速讯
RTX 4060 Ti/RX 7600新卡同时来了!NVIDIA、AMD拼价格:玩家血赚|每日视点
资讯:3年10倍!插电车型翻倍式爆卖 比亚迪独占世界4成
比5G好用10倍!北京首个5.5G实验基站正式开通
开除软件部门全体高管后 要用华为车机软件?大众回应
国家一级保护动物黄嘴白鹭“组团”造访厦门|世界快消息
kasini3000新增:ansible like输出 观焦点
小程序安全架构分析
天天微头条丨Natasha相关辅助类 (六)
SpringBoot项目预加载数据——ApplicationRunner、CommandLineRunner、InitializingBean 、@PostCo
DQL语句(一) -----简单select查询
过氧化苯甲酸叔丁酯商品报价动态(2023-05-17)
收评:沪指跌0.21%量能创逾两月新低 军工板块表现活跃
春秋时期青铜器酷似5根天线路由器 用途至今成谜 环球热头条
今日国内油价迎年内最大降幅 下次调整或将持续下跌
世界快资讯丨4年磨一剑 华为麒麟A2芯片来了:消息称已具备量产能力
全球看热讯:不负等待 新款华为MateBook E 二合一笔记本或将开启移动生产力融合时代
奥迪前CEO承认“大众排放门”存欺诈 拿839万元换缓刑
海上航行26天,首单到货!以人民币结算
SRE Google 运维解密读书笔记一:SRE 方法论概述
Python从零到壹丨带你了解图像直方图理论知识和绘制实现
匠心精神--来看一个小迭代的代码实现
《塞尔达传说:王国之泪》卖爆背后是手游行业的悲哀-全球视讯
再见吧!特斯拉强制单踏板模式
全球今亮点!“感染”塞尔达病毒后 我每天只睡三小时
硬派越野车不适合城市?仰望U8云辇-P出手:三级可调、软硬随心
ChatGPT估值已上2000亿 创始人对钱没兴趣:收入只够交保险 当前速看
23中国中药SCP001今日发布发行公告|当前聚焦
全球头条:1美金等于多少人民币元(2023年5月17日)
国内又现被驱赶的5G基站:你敢建?我就敢拆!居民称辐射大有害健康|全球热点评