BlockCanary分析android卡顿

2024-06-19 12:58

本文主要是介绍BlockCanary分析android卡顿,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/bazhongren/article/details/51125113
http://blog.csdn.net/bazhongren/article/details/51125113 


BlockCanary分析android卡顿

标签: android优化ANR-卡顿
  240人阅读  评论(0)  收藏  举报
  分类:

目录(?)[+]

BlockCanary分析android卡顿

在复杂的项目环境中,由于历史代码庞大,业务复杂,包含各种第三方库,所以在出现了卡顿的时候,我们很难定位到底是哪里出现了问题,即便知道是哪一个Activity/Fragment,也仍然需要进去里面一行一行看,动辄数千行的类再加上跳来跳去调来调去的,结果就是不了了之随它去了,实在不行了再优化吧。于是一拖再拖,最后可能压根就改不动了,客户端越来越卡。

Android应用卡顿是非常普遍的现象,偶尔出现ANR。只有当APP出现ANR,我们才能得到当前堆栈信息。当应用只是卡顿或只是不太流畅的时候,我们能不能找出卡顿元凶呢?不依赖Debug和源码的情况,能不能找出卡顿的堆栈信息呢?我们需要找到一种方法来检测哪些函数可能会使应用发生ANR,在开发阶段就能找出卡顿元凶,提高应用流畅度。

BlockCanary就是来解决这个问题的。告别打点,告别Debug,哪里卡顿,一目了然,让优化代码变得有的放矢。BlockCanary 地址:https://github.com/moduth/blockcanary

一. ANR原理

在Android中所有KeyEvent和TouchEvent都是按照先后顺序放入队列中,依次执行,并且只有当前一个事件执行完毕,才能开始执行下一个事件。每个正在执行的事件都被被保存在waitQueue中,执行完毕之后从waitQueue中移除。

当用户触发一个事件的时候,首先判断waitQueue是否为空,如果为空,可以立即响应该事件。如果队列不为空,说明还有事件没有执行完毕。判断当前时间和上一个事件响应时间是否大于超时时间(一般5秒,broadCastReceiver 10秒),如果超时,则会通过ActivityManagerService以弹窗的形式通知用户。 
这里写图片描述

二. 卡顿(ANR)检测原理

从 ANR原理 可以知道,当一个事件处理时间超过阈值就会触发ANR。Android中所有事件(包括Activity,Service生命周期管理)都是通过Looper+MessageQueue+Handler来处理的。所有事件都被封装成Message,然后被放入MessageQueue中,Looper负责将Message交给对应的Handler去处理。

Looper是一个死循环,通过dispatchMessage分发Message到对应Handler中处理。我们可以近似认为dispatchMessage花费的时间就是每条消息处理时间。因此,我们只要记录每个Mesage dispatchMessage的执行时间,就可以得到事件花费的时间。

从Looper源码中我们发现,执行dispatchMessage前后都有一个logging打印,并且Looper提供了注册logging的方法。所有我们可以在MainThread Looper中注册一个logging,在每条消息dispatchMessage前后,都能收到一条打印记录。通过记录dispatchMessage之前的时间t1和dispatchMessage执行之后的log时间t2,totalTime = t2-t1得到该事件执行时间。

Looper.Java

looper

looper

三. BlockCanary卡顿检测流程

这里写图片描述 
BlockCanary启动一个线程负责保存UI线程当前堆栈信息,将堆栈信息以及CPU信息保存分别保存在 mThreadStackEntries和mCpuInfoEntries中,每条信息都以时间撮为key保存。

BlockCanary注册了logging来获取事件开始结束时间。如果检测到事件处理时间超过阈值(默认值1s),则从mThreadStackEntries中查找T1~T2这段时间内的堆栈信息,并且从mCpuInfoEntries中查找T1~T2这段时间内的CPU及内存信息。并且将信息格式化后保存到本地文件,并且通知用户。

四. BlockCanary使用

BlockCanary sdk在jcenter 中,目前最新的版本为1.2.0。

(1) 修改gradle

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">allprojects {repositories {mavenCentral()jcenter()}
}dependencies {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// most often used way, enable notification to notify block event</span>compile <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.github.moduth:blockcanary-ui:1.2.0'</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// this way you only write block logs, without notification</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// compile 'com.github.moduth:blockcanary-android:1.2.0'</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// this way you only enable BlockCanary in debug package</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// debugCompile 'com.github.moduth:blockcanary-ui:1.2.0'</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// releaseCompile 'com.github.moduth:blockcanary-no-op:1.2.0'</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li></ul>

(2) 注册BlockCanary

在自定义application中注册。

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">DemoApplication</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Application</span> {</span><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>() {...<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Do it on main process</span>BlockCanary.install(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AppBlockCanaryContext()).start();}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>

通过以上两个步骤,我们就能使用BlockCanary了。

(3) 修改事件超时阈值

事件处理时间阈值默认为1000ms,我们可以通过修改BlockCanaryContext.java 的getConfigBlockThreshold方法修改事件处理超时阈值。

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*** Config block threshold (in millis), dispatch over this duration is regarded as a BLOCK. You may set it* from performance of device.**<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span> threshold in mills*/</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getConfigBlockThreshold</span>() {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>;}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

五 BlockCanary参数解读

这里写图片描述

cpuCore:手机cpu个数。 
processName:应用包名。 
freeMemory: 手机剩余内存,单位KB。 
totalMemory: 手机内训总和,单位KB。

timecost: 该Message(事件)执行时间,单位 ms。 
threadtimecost: 该Message(事件)执行线程时间(线程实际运行时间,不包含别的线程占用cpu时间),单位 ms。

cpubusy: true表示cpu负载过重,false表示cpu负载不重。cpu负载过重导致该Message(事件) 超时,错误不在本事件处理上。

cpuBusy判断:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">CpuSampler.java<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">CpuSampler</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> sampleIntervalMillis) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(sampleIntervalMillis);BUSY_TIME = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) (mSampleIntervalMillis * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.2</span>f);}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">isCpuBusy</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> start, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> end) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (end - start > mSampleIntervalMillis) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> s = start - mSampleIntervalMillis;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> e = start + mSampleIntervalMillis;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> last = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (mCpuInfoEntries) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Map.Entry<Long, String> entry :mCpuInfoEntries.entrySet()) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> time = entry.getKey();<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (s < time && time < e) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (last != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> && time - last > BUSY_TIME) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;}last = time;}}}}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>

BUSY_TIME = (int) (mSampleIntervalMillis * 1.2f); 
CpuSampler在子线程中每隔mSampleIntervalMillis读取一次cpu信息,如果在BUSY_TIME 没有读取下一条cpu信息,表示cpu忙,来不及处理本pid的任务,导致应用出现超时。

这篇关于BlockCanary分析android卡顿的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075096

相关文章

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用