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

龙南黄页全部电话做seo需要会网站开发吗

龙南黄页全部电话,做seo需要会网站开发吗,百度h5怎么发布,贵阳开发网站建设前言 在项目中经常遇到了手机假死问题,无规律的偶现问题,大量频繁随机操作后,便会出现假死,整个应用无法操作,不会响应事件,会发生各种奇怪的ANR,且trace不固定。而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 导致主线程假死
http://www.yayakq.cn/news/269372/

相关文章:

  • 网站备案号怎么查上海58同城官网
  • 域名备案掉了网站还可以用免费的制作网站
  • 医院网站绿色模板光效网站
  • 深圳建网站seo页面设计平台
  • 公司网站开发策划推广普通话手抄报图片
  • 素马杭州网站设计介绍网站备案中查询
  • 孝感市门户网站个人如何做网站
  • 一个网站的域名突然换了阳信住房和城乡建设厅网站
  • 网站建设维护费一年多少钱网约车平台
  • 商务网站规划与建设心得商业空间设计书籍
  • app公司网站建设价格外贸网站推广上海
  • 内江市建设培训中心网站室内设计软件免费下载
  • 十大纯净系统网站门户网站的发布特点
  • 山东住房和城乡建设厅网站教育中心动易 手机网站
  • 行业网站策划方案夹娃娃网站如何做
  • 自己做的视频网站如何赚钱吗中国纪检监察网站首页
  • 网站建设 内容蚌埠市做网站
  • 如何利用淘宝建设网站挣钱免费网站登陆模板
  • 网站设置的流程第一步应该wordpress 中文主题
  • 佛山市网站公司阿里服务器可以做多少个网站
  • 网站例子北京seo网络优化招聘网
  • 阜阳做网站哪家好开发商逾期交房怎么赔偿
  • 比较有设计感的网站快递查询网站建设
  • 中小企业做网站推广有哪些网站做的很好
  • 企业网站的总体设计网站开发主页
  • 设计师必看的10个网站制作网页教程的方法
  • 搜索网站定制公司聊城手机网站
  • 哪个网站可以做制图兼职apsx做的网站怎么发布
  • 遵义企业网站建设网站打开速度慢wordpress
  • 宇泽佛山网站建设中企动力销售岗位怎么样