当前位置: 首页 > news >正文

ANR系列(二)——ANR监听方案之SyncBarrier

前言

在项目中经常遇到了手机假死问题,无规律的偶现问题,大量频繁随机操作后,便会出现假死,整个应用无法操作,不会响应事件,会发生各种奇怪的ANR,且trace不固定。而SyncBarrier是其中的罪魁祸首之一

SyncBarrier的介绍

SyncBarrier大家又称它为同步屏障,这是安卓线程消息队列里面的一个新增加的东西,它是一种Handler中的同步屏障机制。简单可以理解安卓在Hanlder的处理上增加了优先级,优先级最高的就是SyncBarrier。

1、消息分类

Handler中的Message可以分为两类:同步消息体(优先级高)、异步消息体(优先级低)。可以通过Message.javaisAsynchronous()知道是否为异步消息体

public boolean isAsynchronous() {return (flags & FLAG_ASYNCHRONOUS) != 0;
}

2、SyncBarrier是什么

SyncBarrier可以通过MessageQueue.postSyncBarrier()发送一个同步消息体,该消息唯一的区别点在于Message没有target

private int postSyncBarrier(long when) {// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized (this) {final int token = mNextBarrierToken++;final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;Message prev = null;Message p = mMessages;if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;}return token;}
}

跟以往相比,以往的Handler发送消息最终都会调用enqueueMessage函数

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;//...return queue.enqueueMessage(msg, uptimeMillis);
}

可以知道enqueueMessage会设置了msg.target = this;,从代码层面上,同步屏障就是一个target字段为空的Message

3、SyncBarrier的作用

  • 当队列中出现SyncBarrier(具体实现上就是Message#target为null)时,就会忽略所有异步消息体,寻找同步消息体,然后优先处理它,这些API全部都是hide的,也就是说app中是无法使用的,谷歌设计初衷也是系统开发人员自己用的
  • 消息队列这东西是在安卓一诞生就有了的东西,大部分时候它也没有什么问题。但有一个事情,就是安卓操作系统的UI流畅度远不及水果平台(iOS),原因就是在于水果平台的UI渲染是整个系统中最高优先执行。于是就有了SyncBarrier机制,这东西就是为了让消息队列有优先级,它发送的消息将会是最高优先级的,会被优先处理,这样来达到UI优先渲染,达到提高渲染速度的目的
Message next() {for (;;) {//......synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;// 1、碰到同步屏障if (msg != null && msg.target == null) {// 2、循环遍历消息链表,在表头插入同步屏障do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {//...} 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();// 3、返回当前的消息return msg;}} else {nextPollTimeoutMillis = -1;}}}
}

可以看到,当设置了同步屏障之后,next()将会忽略所有的异步消息体,返回同步屏障消息。

4、SyncBarrier的发送

通常我们使用Handler发消息时,这些消息都是同步消息体,如果我们想发送异步消息体,那么在创建Handler时使用以下构造函数中的其中一种(async传true),通过该Handler发送的所有消息都会变成异步消息体

public Handler() {this(null, false);
}public Handler(boolean async) {this(null, async);
}public Handler(@NonNull Looper looper) {this(looper, null, false);
}

5、SyncBarrier的应用

前面说到SyncBarrier并不是给app开发同学用的,很多相关的接口并没有开放出来,这是为了提高UI渲染而设计的东西。因此这东西主要是用在了UI渲染过程中。仔细查看ViewRootImpl的源码可以发现,每次渲染View之前都会先给主线程插入SyncBarrier,以挡住异步消息体,保证渲染被主线程优先执行

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;// 1、设置同步障碍,确保mTraversalRunnable优先被执行mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// 2、内部通过Handler发送了一个异步消息mTraversalRunnablemChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}void unscheduleTraversals() {if (mTraversalScheduled) {mTraversalScheduled = false;//移除同步障碍mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);}
}// 3、mTraversalRunnable最终执行到这里
void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);// 4、执行measure、layout、drawperformTraversals();}
}

6、SyncBarrier的泄露

Barrier消息像一道栅栏,将消息队列里的普通消息先拦住,多数情况下是正常,但一旦异常,则很容易发生ANR,且ANR的trace都是莫名其妙的,但是也有些情况,是Barrier引起的trace就停在nativePollOnce(),当然这里指的是小部分情况,而非所有的nativePollOnce()都是SyncBarrier引起的,具体情况具体分析

  • 正常情况:渲染刷新类先优先执行,等执行完以后,撤掉栅栏,普通消息(包括会导致ANR的消息)得以继续执行
  • 异常情况:Barrier存在泄漏,导致无法释放栅栏,普通消息卡住不动,UI假死,如果期间有Server或者Provider等消息超时,就会引发ANR

一旦发生Barrier的泄露,在取消息的时候优先进入同步屏障的逻辑,主线程会过滤掉所有非异步消息!msg.isAsynchronous(),一直在死循环中出不来,只有移除当前的同步屏障后,才得以解开

if (msg != null && msg.target == null) {do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());
}

如下图,正常情况下是执行1,4,5,2,3,6,而异常情况是Barrier在此没有被移除,导致2,3,6都无法执行

在这里插入图片描述

7、SyncBarrier的问题

SyncBarrier产生的问题往往是异步刷新导致的,比如:子线程触发invalidate()UI频繁更新,自定义View写法不对,surfaceview异步刷新等等

在这里插入图片描述

从上图可以看出,如果子线程同时多次进入mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();那么就会发送多个Barrier,但是在移除掉的时候,只移除当前成员变量mTraversalBarrier一个Barrier,多余的就会导致泄露

在这里插入图片描述

8、SyncBarrier的模拟问题

1.创建子线程频繁刷新UI的自定义View

class ThreadView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {var i = 1Fvar paint = Paint()fun start() {object : Thread("funny1") {override fun run() {super.run()while (true) {invalidate()}}}.start()}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {setMeasuredDimension(getDefaultSize(suggestedMinimumWidth, widthMeasureSpec),getDefaultSize(suggestedMinimumHeight, heightMeasureSpec))}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)i += 100fif (i >= 2000f) {i = 0f}paint.setAntiAlias(false)paint.setColor(Color.BLACK)paint.setStrokeWidth(3f)canvas?.drawCircle(i, i, 90f, paint)}
}

2.主线程频繁随机更新背景色

var runningThread = object : Runnable {val random = Random()override fun run() {val r: Int = random.nextInt(256)val g: Int = random.nextInt(256)val b: Int = random.nextInt(256)rootView?.setBackgroundColor(Color.rgb(r, g, b))mHandler.postDelayed(this, 100)}
}

3.通过设置Looper取消息的接口Looper.getMainLooper().setMessageLogging(CustomPrinter()),输出我们想要的日志

public void println(String reason) {if (reason.charAt(0) == '>') {Message message = getMessage();if (message.getTarget() == null) {Log.e("Hensen", "[token=" + message.arg1 + "]" + "[target=" + message.getTarget() + "]  [when=" + message.getWhen() + "]" + "[next=" + getNext(message) + "]");}}
}

4.运行效果,当背景色卡住的时候,此时主线程明显被阻塞,也就是说Barrier泄漏的现场

在这里插入图片描述

5.在卡住的时候,通过日志也可以看得出来当前looper消息一直卡在[token=41208][target=null]的消息中,该消息就是Barrier

2023-03-02 17:05:47.005 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s563ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.011 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s569ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.022 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s580ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.031 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s589ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.038 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s597ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.045 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s603ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]

9、SyncBarrier问题的解决

我们通过反射MainLooper的mMessage,如果当前的Message的target为null,并且这个Message的when时间已经存在很久的话,这个时候我们会怀疑产生了SyncBarrier的泄漏。但此时还不能完全确定,因为如果当时因为其他原因导致主线程卡死,也可能会导致这种现象。然后,我们再起一个handler往MainLooper发送一个同步消息和一个异步消息,并且发两次doublecheck。如果异步消息被处理了,但是同步消息一直无法被处理,这时候就说明产生了SyncBarrier的泄漏。

1.通过Timer启动一个1s的轮询任务

private fun startCheckBarrier() {val checkBarrierTimer = Timer("syncBarrier")checkBarrierTimer.schedule(AutoCheckerTask(), 20000L, 1000L)
}

2.每次轮询都会检查下当前的消息队列中是否有超过3starget=null的消息,这里作为演示就直接移除了

class AutoCheckerTask : TimerTask() {@RequiresApi(api = 23)override fun run() {Log.e("KKK", "detectSyncBarrierMessage")detectSyncBarrierMessage()}@RequiresApi(Build.VERSION_CODES.M)fun detectSyncBarrierMessage() {try {val mainQueue = Looper.getMainLooper().queueval field = mainQueue.javaClass.getDeclaredField("mMessages")field.isAccessible = trueval mMessage = field[mainQueue] as Messageif (mMessage != null) {val `when` = SystemClock.uptimeMillis() - mMessage.getWhen()if (`when` > 3000L && mMessage.target == null) {val token = mMessage.arg1this.removeSyncBarrier(token)}}} catch (var7: Exception) {Log.e("SyncBarrierMonitor", var7.toString())}}@RequiresApi(api = 23)fun removeSyncBarrier(token: Int) {try {val mainQueue = Looper.getMainLooper().queueval method =mainQueue.javaClass.getDeclaredMethod("removeSyncBarrier", Integer.TYPE)method.isAccessible = truemethod.invoke(mainQueue, token)Log.e("KKK", "detectSyncBarrierMessage [token=" + token + "]")} catch (var4: java.lang.Exception) {Log.e("SyncBarrierMonitor", var4.toString())}}
}

3.通过日志可以看出,移除了当前Barrier之后,主线程也恢复了运行,背景色开始又闪烁起来了

2023-03-02 17:05:47.038 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s597ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.045 8502-8502/com.example.syncbarriermonitor E/Hensen: [token=41208][target=null]  [when=5672519349][next={ when=-6s603ms callback=com.example.syncbarriermonitor.MainActivity$runningThread$1 target=android.os.Handler }]
2023-03-02 17:05:47.054 8502-8538/com.example.syncbarriermonitor E/KKK: detectSyncBarrierMessage
2023-03-02 17:05:47.054 8502-8538/com.example.syncbarriermonitor E/KKK: detectSyncBarrierMessage [token=41208]

参考资料

  • Handler之同步屏障机制(sync barrier)
  • Android Sync Barrier机制
  • 让 nativePollOnce 不再排名第一 | 钉钉 ANR 治理最佳实践
  • 今日头条 ANR 优化实践系列 - Barrier 导致主线程假死

相关文章:

ANR系列(二)——ANR监听方案之SyncBarrier

前言 在项目中经常遇到了手机假死问题&#xff0c;无规律的偶现问题&#xff0c;大量频繁随机操作后&#xff0c;便会出现假死&#xff0c;整个应用无法操作&#xff0c;不会响应事件&#xff0c;会发生各种奇怪的ANR&#xff0c;且trace不固定。而SyncBarrier是其中的罪魁祸首…...

【完美解决】应用程序无法正常启动(0xc000007b)请单击“确定”关闭应用程序

年期安装CorelDRAW X8 (64-Bit)&#xff0c;安装完成之后运行一点毛病都没有&#xff0c;可是过了两三个月&#xff0c;再打开就出现“应用程序无法正常启动(0xc000007b)请单击“确定”关闭应用程序”这个提示框&#xff0c;如下图示 出现这个问题我就上网查找&#xff0c;无非…...

.NET基础加强第二课--静态成员,静态类

类 实例类 默认是实例类 静态类 在类前加上static ,就是静态类 静态类中&#xff0c;所有包含的成员必须是静态成员 实例成员是属于具体某个对象的 举例代码 Person p1 new Person(); p1.Age 20; p1.Name “张三”; class Person { public string Name { get; set;…...

【UML+OOPC嵌入式C语言开发】使用C语言实现一个面向对象语言才能够实现的类

文章目录简述OOPC开发环境知识讲解函数示例类的实现示例接口实现示例&#xff08;前面两部分有点无聊&#xff0c;如果大家没兴趣看可以直接从知识讲解开始看&#xff09; 简述OOPC oopc&#xff0c;是一种轻量级的面向对象的C语言编程框架&#xff0c; LW_OOPC是Light-Weight …...

软件测试自动化Java篇【Selenium+Junit 5】

文章目录Selenium环境部署自动化测试例子常见的元素操作窗口等待浏览器的操作弹窗选择器执行脚本文件上传浏览器参数Junit 5导入依赖Junit 4 和 Junit5 注解对比断言测试顺序参数化单参数多参数动态参数测试套件指定类来运行测试用例指定包名来运行包下测试用例Selenium 为什么…...

Clip:学习笔记

Clip 文章目录Clip前言一、原理1.1 摘要1.2 引言1.3 方法1.4 实验1.4.1 zero-shot Transfer1.4.2 PROMPT ENGINEERING AND ENSEMBLING1.5 局限性二、总结前言 阅读论文&#xff1a; Learning Transferable Visual Models From Natural Language Supervision CLIP 论文逐段精读…...

STM32CubexMX与FreeRTOS学习

目录 LED与EXTI配置 基本定时器使用 软件定时器 在HAL库中实现printf 重点--记得自己添加头文件 队列实现 二值信号量实现 计数信号量实现 DMA实现 ADC配置 RTC配置 看门狗 窗口看门狗 FreeRTOS结合MX软件开发&#xff0c;基础配置直接生成&#xff0c;我们只…...

Master Slave 主从同步错误 Slave_IO_Running:NO/Slave_SQL_Running: No

Master Slave 主从同步错误 Slave_IO_Running:NO Slave_SQL_Running:Yes #在Slave库上查看状态 mysql> show slave status\G Slave_IO_Running: No Slave_SQL_Running: Yes #重启master库&#xff1a;service mysqld restart mysql> show master status; ------------…...

JavaScript函数之prototype原型和原型链

文章目录1. 原型2. 显式和隐式原型3. 原型链3.1 访问顺序4. instanceof4.1 如何判断1. 原型 函数的prototype属性 每个函数都有一个prototype属性&#xff0c;它默认指向一个Object空对象&#xff08;即&#xff1a;原型对象&#xff09;。原型对象中有一个属性constructor&a…...

从上海分时电价机制调整看转供电用户电能计费

安科瑞 耿敏花2022年12月16日&#xff0c;上海市发改委发布《关于进一步完善我市分时电价机制有关事项的通知》(沪发改价管〔2022〕50号)。通知明确上海分时电价机制&#xff0c;一般工商业及其他两部制、大工业两部制用电夏季&#xff08;7、8、9月&#xff09;和冬季&#xf…...

TypeScript类型体操:获取数组中元素对象属性的值作为新类型

title: TypeScript类型体操&#xff1a;获取数组中元素对象属性的值作为新类型 date: 2023-03-03 20:58:24 categories: TypeScript类型体操 tags: TypeScript类型体操TypeScript 首先先说获取数组中元素对象属性的值作为新类型的解决方案 使用 as const 强调不可变数组使用 …...

npm,yarn和pnpm

npm扁平的node_modules结构比如项目依赖了A 和 C&#xff0c;而 A 和 C 依赖了不同版本的 B1.0 和 B2.0&#xff0c;D也依赖B1.0, node_modules 结构如下&#xff1a;node_modules ├── A1.0.0 ├── B1.0.0 └── C1.0.0└── node_modules└── B2.0.0C依赖的B2.0因为版…...

【算法】【数组与矩阵模块】在排好序的矩阵中找数,时间复杂度O(M+N)

目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过&#xff0c;但是不保证100%的测试用例&#xff0c;如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识&#xff01; 问题介绍 …...

【Java|基础篇】计算机中数据的存储规则

文章目录前言:1.计算机中的数据2.二进制的介绍二进制的运算规则常见的进制3.字符的存储4.汉字的存储5.图片的存储6.音频的存储总结:前言: 本篇文章只是为了科普 计算机中数据的存储规则 1.计算机中的数据 计算机的数据大致分为三类:文本数据,图片和音频 注:视频是图片和音频…...

RestTemplate使用HttpClient连接池

文章目录RestTemplate使用HttpClient连接池ClientHttpRequestFactorySimpleClientHttpRequestFactorySimpleClientHttpRequestFactory 设置超时时间HttpURLConnection的缺点HttpComponentsClientHttpRequestFactoryPoolingHttpClientConnectionManager配置连接池HttpClient总结…...

Python 操作Redis

在 Python中我们使用 redis库来操作 Redis数据库。Redis数据库的使用命令这里就不介绍了。 需要安装 redis库。检查是否安装redis&#xff1a; pip redis 如果未安装&#xff0c;使用 pip命令安装 redis。 pip install redis #安装最新版本 一、Redis连接 Redis提供两个类 Re…...

CEC2020:鱼鹰优化算法(Osprey optimization algorithm,OOA)求解CEC2020(提供MATLAB代码

一、鱼鹰优化算法简介 鱼鹰优化算法&#xff08;Osprey optimization algorithm&#xff0c;OOA&#xff09;由Mohammad Dehghani 和 Pavel Trojovsk于2023年提出&#xff0c;其模拟鱼鹰的捕食行为。 鱼鹰是鹰形目、鹗科、鹗属的仅有的一种中型猛禽。雌雄相似。体长51-64厘米…...

词对齐 - MGIZA++

文章目录关于 MGIZAgiza-py安装 MGIZA命令说明mkclsd4normhmmnormplain2sntsnt2coocsnt2coocrmpsnt2plainsymalmgizageneral parameters:No. of iterations:parameter for various heuristics in GIZA for efficient training:parameters for describing the type and amount o…...

GUI 之 Tkinter编程

GUI 图形界面&#xff0c;Tkinter 是 Python 内置的 GUI 库&#xff0c;IDLE 就是 Tkinter 设计的。 1. Tkinter 之初体验 import tkinter as tkroot tk.Tk() # 创建一个窗口root.title(窗口标题)# 添加 label 组件 theLabel tk.Label(root, text文本内容) theLabel.p…...

【软件测试】性能测试面试题都问什么?面试官想要什么?回答惊险避坑......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 1、你认为不同角色关…...

后端开发基础能力以及就Java的主流开发框架介绍

前言&#xff1a;java语言开发转后端&#xff0c;必须了解后端主流的一些东西&#xff0c;共勉。 后端开发需要具备以下基础能力&#xff1a; 1.编程语言&#xff1a;熟练掌握至少一门编程语言&#xff0c;如Java、Python、Ruby、PHP、C#等。 2.数据结构和算法&#xff1a;具…...

H2数据库连接时用户密码错误:Wrong user name or password [28000-214] 28000/28000 (Help)

H2数据库连接时用户密码错误: 2023-03-03 08:25:07 database: wrong user or password; user: "SA" org.h2.message.DbException: Wrong user name or password [28000-214]出现的问题配置信息原因解决办法org.h2.message.DbException: Wrong user name or password …...

青岛诺凯达机械盛装亮相2023济南生物发酵展,3月与您相约

BIO CHINA生物发酵展&#xff0c;作为生物发酵产业一年一度行业盛会&#xff0c;由中国生物发酵产业协会主办&#xff0c;上海信世展览服务有限公司承办&#xff0c;2023第10届国际生物发酵展&#xff08;济南&#xff09;于2023年3月30-4月1日在山东国际会展中心&#xff08;济…...

【JAVA程序设计】【C00111】基于SSM的网上图书商城管理系统——有文档

基于SSM的网上图书商城管理系统——有文档项目简介项目获取开发环境项目技术运行截图项目简介 基于ssm框架开发的网上在线图书售卖商城项目&#xff0c;本项目分为三种权限&#xff1a;系统管理员、卖家、买家 管理员角色包含以下功能&#xff1a; 用户信息管理、权限管理、订…...

基于卷积神经网络CNN的三相故障识别

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 卷积神经网络CNN手写体识别 基本结构 主要参数 MATALB代码 结果图 展望 背影 现在生活&#xff0c;为节能减排&#xff0c;减少电能损…...

Java工厂设计模式详解,大厂的Java抽象工厂模式分享!

我是好程序员-小源&#xff01;本期文章主要给大家分享&#xff1a;Java工厂设计模式。文中使用通俗易懂的案例&#xff0c;使你快速学习和轻松上手&#xff01;一、什么是Java抽象工厂模式1. Java抽象工厂是23种设计模式中创建型模式的一种&#xff0c;Java抽象工厂是由多个工…...

Git 企业级分支提交流程

Git 企业级分支提交流程 首先在本地分支hfdev上进行开发&#xff0c;开发后要经过测试。 如果测试通过了&#xff0c;那么久可以合并到本地分支develop&#xff0c;合并之后hfdev和development应该完全一样。 git add 文件 git commit -m ‘注释’ git checkout develop //切换…...

C/C++每日一练(20230303)

目录 1. 字符串相乘 2. 单词拆分 II 3. 串联所有单词的子串 1. 字符串相乘 给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 示例 1: 输入: num1 "2", num2 "3"…...

Python3-条件控制

Python3 条件控制 Python 条件语句是通过一条或多条语句的执行结果&#xff08;True 或者 False&#xff09;来决定执行的代码块。 可以通过下图来简单了解条件语句的执行过程: 代码执行过程&#xff1a; if 语句 Python中if语句的一般形式如下所示&#xff1a; if condi…...

KDZD地埋电缆故障测试仪

一、产品特性 ★电缆故障测试仪&#xff08;闪测仪&#xff09; &#xff08;1&#xff09;使用范围广&#xff1a;用于测量各种不同截面、不同介质的各种电力电缆、高频同轴电缆&#xff0c;市话电缆及两根以上均匀铺设的地埋电线等电缆高低阻、短路、开路、断线以及高阻泄漏…...

闵行做网站公司/海淀区seo多少钱

2020年8月12日至13日&#xff0c;由中国航空学会和中国航空研究院联合主办的第九届民用飞机航电国际论坛在上海顺利召开&#xff01;图&#xff1a;雷达所民机中心参会人员该论坛是国内最高水平的民用飞机航电论坛。航空工业雷达所作为论坛组委会主要成员单位参与本次论坛。雷达…...

PHP做网站的核心是什么/seo怎么优化网站排名

在 ActiveReports 中&#xff0c;可以固定报表每页显示的行数&#xff0c;当每页的数据不足固定的行数时&#xff0c;自动通过填补空白行来实现&#xff0c;当然这两种功能仅限于区域报表和页面报表中。 区域报表 在区域报表中&#xff0c;有很多方法来控制报表显示的行数 1. 在…...

做挂件的网站/推广平台下载

软件系统体系结构 1 常见软件系统体系结构B/S、C/S 1.1 C/S l C/S结构即客户端/服务器&#xff08;Client/Server&#xff09;&#xff0c;例如QQ&#xff1b; l 需要编写服务器端程序&#xff0c;以及客户端程序&#xff0c;例如我们安装的就是QQ的客户端程序&#…...

广州网站导航/电脑优化

MT6735 PCB设计规范: 本篇资料内容包括: 概述 封装 • 6735芯片外形尺寸 • MT6735 Footprint设计 • MT6735重要信号分布图 一般设计建议 • 叠构(PCB stack‐up))建议建议 • Common Rules and Via Type • Placement Notes • MT6735 fan out High‐Spee…...

监控摄像头做斗鱼直播网站/百度seo推广首选帝搜软件

20172310 2017-2018《程序设计与数据结构》&#xff08;下&#xff09;第八周学习总结 教材学习内容总结 1.1、堆 堆(heap)&#xff1a;是具有两个附加属性的二叉树。一是堆是一颗完全树&#xff08;如果一棵二叉树是平衡的&#xff0c;即所有叶子都位于h或h-1层&#xff0c;其…...

做网站需要准备哪些材料/太原百度快照优化排名

http://bubkoo.com/2015/05/08/introduction-to-fetch/ 转载于:https://www.cnblogs.com/tianlai/p/4889555.html...