日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

IOS中判斷卡頓的方案總結

瀏覽:136日期:2022-09-16 17:31:22
FPS

FPS (Frames Per Second) 是圖像領域中的定義,表示每秒渲染幀數,通常用于衡量畫面的流暢度,每秒幀數越多,則表示畫面越流暢,60fps 最佳,一般我們的APP的FPS 只要保持在 50-60之間,用戶體驗都是比較流暢的。

監測FPS也有好幾種,這里只說最常用的方案,我最早是在YYFPSLabel中看到的。實現原理實現原理是向主線程的RunLoop的添加一個commonModes的CADisplayLink,每次屏幕刷新的時候都要執行CADisplayLink的方法,所以可以統計1s內屏幕刷新的次數,也就是FPS了,下面貼上我用Swift實現的代碼:

class WeakProxy: NSObject {weak var target: NSObjectProtocol?init(target: NSObjectProtocol) {self.target = targetsuper.init() }override func responds(to aSelector: Selector!) -> Bool {return (target?.responds(to: aSelector) ?? false) || super.responds(to: aSelector) }override func forwardingTarget(for aSelector: Selector!) -> Any? {return target }}class FPSLabel: UILabel {var link:CADisplayLink!//記錄方法執行次數var count: Int = 0//記錄上次方法執行的時間,通過link.timestamp - _lastTime計算時間間隔var lastTime: TimeInterval = 0var _font: UIFont!var _subFont: UIFont! fileprivate let defaultSize = CGSize(width: 55,height: 20)override init(frame: CGRect) {super.init(frame: frame)if frame.size.width == 0 && frame.size.height == 0 {self.frame.size = defaultSize}self.layer.cornerRadius = 5self.clipsToBounds = trueself.textAlignment = NSTextAlignment.centerself.isUserInteractionEnabled = falseself.backgroundColor = UIColor.white.withAlphaComponent(0.7)_font = UIFont(name: 'Menlo', size: 14)if _font != nil { _subFont = UIFont(name: 'Menlo', size: 4)}else{ _font = UIFont(name: 'Courier', size: 14) _subFont = UIFont(name: 'Courier', size: 4)}link = CADisplayLink(target: WeakProxy.init(target: self), selector: #selector(FPSLabel.tick(link:)))link.add(to: RunLoop.main, forMode: .commonModes) }//CADisplayLink 刷新執行的方法@objc func tick(link: CADisplayLink) {guard lastTime != 0 else { lastTime = link.timestampreturn}count += 1let timePassed = link.timestamp - lastTime//時間大于等于1秒計算一次,也就是FPSLabel刷新的間隔,不希望太頻繁刷新guard timePassed >= 1 else {return}lastTime = link.timestamplet fps = Double(count) / timePassedcount = 0let progress = fps / 60.0let color = UIColor(hue: CGFloat(0.27 * (progress - 0.2)), saturation: 1, brightness: 0.9, alpha: 1)let text = NSMutableAttributedString(string: '(Int(round(fps))) FPS')text.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: NSRange(location: 0, length: text.length - 3))text.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.white, range: NSRange(location: text.length - 3, length: 3))text.addAttribute(NSAttributedStringKey.font, value: _font, range: NSRange(location: 0, length: text.length))text.addAttribute(NSAttributedStringKey.font, value: _subFont, range: NSRange(location: text.length - 4, length: 1))self.attributedText = text }// 把displaylin從Runloop modes中移除deinit {link.invalidate() }required init?(coder aDecoder: NSCoder) {fatalError('init(coder:) has not been implemented') }}RunLoop

其實FPS中CADisplayLink的使用也是基于RunLoop,都依賴main RunLoop。我們來看看

先來看看簡版的RunLoop的代碼

// 1.進入loop__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled)// 2.RunLoop 即將觸發 Timer 回調。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);// 3.RunLoop 即將觸發 Source0 (非port) 回調。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);// 4.RunLoop 觸發 Source0 (非port) 回調。sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle)// 5.執行被加入的block__CFRunLoopDoBlocks(runloop, currentMode);// 6.RunLoop 的線程即將進入休眠(sleep)。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);// 7.調用 mach_msg 等待接受 mach_port 的消息。線程將進入休眠, 直到被下面某一個事件喚醒。__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort)// 進入休眠// 8.RunLoop 的線程剛剛被喚醒了。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting// 9.如果一個 Timer 到時間了,觸發這個Timer的回調__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())// 10.如果有dispatch到main_queue的block,執行bloc __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);// 11.如果一個 Source1 (基于port) 發出事件了,處理這個事件__CFRunLoopDoSource1(runloop, currentMode, source1, msg);// 12.RunLoop 即將退出__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

我們可以看到RunLoop調用方法主要集中在kCFRunLoopBeforeSources和kCFRunLoopAfterWaiting之間,有人可能會問kCFRunLoopAfterWaiting之后也有一些方法調用,為什么不監測呢,我的理解,大部分導致卡頓的的方法是在kCFRunLoopBeforeSources和kCFRunLoopAfterWaiting之間,比如source0主要是處理App內部事件,App自己負責管理(出發),如UIEvent(Touch事件等,GS發起到RunLoop運行再到事件回調到UI)、CFSocketRef。開辟一個子線程,然后實時計算 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting 兩個狀態區域之間的耗時是否超過某個閥值,來斷定主線程的卡頓情況。

這里做法又有點不同,iOS實時卡頓監控3 是設置連續5次超時50ms認為卡頓,戴銘在 GCDFetchFeed4 中設置的是連續3次超時80ms認為卡頓的代碼。以下是iOS實時卡頓監控中提供的代碼:

- (void)start{if (observer)return;// 信號 semaphore = dispatch_semaphore_create(0);// 注冊RunLoop狀態觀察CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL}; observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, &runLoopObserverCallBack, &context);CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);// 在子線程監控時長dispatch_async(dispatch_get_global_queue(0, 0), ^{while (YES){long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC));if (st != 0) {if (!observer){ timeoutCount = 0; semaphore = 0; activity = 0;return;}if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting){if (++timeoutCount < 5)continue; PLCrashReporterConfig *config = [[PLCrashReporterConfig alloc] initWithSignalHandlerType:PLCrashReporterSignalHandlerTypeBSD symbolicationStrategy:PLCrashReporterSymbolicationStrategyAll]; PLCrashReporter *crashReporter = [[PLCrashReporter alloc] initWithConfiguration:config];NSData *data = [crashReporter generateLiveReport]; PLCrashReport *reporter = [[PLCrashReport alloc] initWithData:data error:NULL];NSString *report = [PLCrashReportTextFormatter stringValueForCrashReport:reporter withTextFormat:PLCrashReportTextFormatiOS];NSLog(@'------------n%@n------------', report);} } timeoutCount = 0;} });}子線程Ping

但是由于主線程的RunLoop在閑置時基本處于Before Waiting狀態,這就導致了即便沒有發生任何卡頓,這種檢測方式也總能認定主線程處在卡頓狀態。這套卡頓監控方案大致思路為:創建一個子線程通過信號量去ping主線程,因為ping的時候主線程肯定是在kCFRunLoopBeforeSources和kCFRunLoopAfterWaiting之間。每次檢測時設置標記位為YES,然后派發任務到主線程中將標記位設置為NO。接著子線程沉睡超時闕值時長,判斷標志位是否成功設置成NO,如果沒有說明主線程發生了卡頓。ANREye5中就是使用子線程Ping的方式監測卡頓的。

@interface PingThread : NSThread......@end@implementation PingThread- (void)main { [self pingMainThread];}- (void)pingMainThread {while (!self.cancelled) {@autoreleasepool {dispatch_async(dispatch_get_main_queue(), ^{[_lock unlock]; });CFAbsoluteTime pingTime = CFAbsoluteTimeGetCurrent();NSArray *callSymbols = [StackBacktrace backtraceMainThread]; [_lock lock];if (CFAbsoluteTimeGetCurrent() - pingTime >= _threshold) {...... } [NSThread sleepForTimeInterval: _interval];} }}@end

以下是我用Swift實現的:

public class CatonMonitor {enum Constants {static let timeOutInterval: TimeInterval = 0.05static let queueTitle = 'com.roy.PerformanceMonitor.CatonMonitor' }private var queue: DispatchQueue = DispatchQueue(label: Constants.queueTitle)private var isMonitoring = falseprivate var semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)public init() {}public func start() {guard !isMonitoring else { return }isMonitoring = truequeue.async {while self.isMonitoring {var timeout = trueDispatchQueue.main.async { timeout = falseself.semaphore.signal()}Thread.sleep(forTimeInterval: Constants.timeOutInterval)if timeout {let symbols = RCBacktrace.callstack(.main)for symbol in symbols {print(symbol.description) }}self.semaphore.wait() }} }public func stop() {guard isMonitoring else { return }isMonitoring = false }}CPU超過了80%

這個是Matrix-iOS 卡頓監控提到的:

我們也認為 CPU 過高也可能導致應用出現卡頓,所以在子線程檢查主線程狀態的同時,如果檢測到 CPU 占用過高,會捕獲當前的線程快照保存到文件中。目前微信應用中認為,單核 CPU 的占用超過了 80%,此時的 CPU 占用就過高了。

這種方式一般不能單獨拿來作為卡頓監測,但可以像微信Matrix一樣配合其他方式一起工作。

戴銘在GCDFetchFeed中如果CPU 的占用超過了 80%也捕獲函數調用棧,以下是代碼:

#define CPUMONITORRATE 80+ (void)updateCPU {thread_act_array_t threads;mach_msg_type_number_t threadCount = 0;const task_t thisTask = mach_task_self();kern_return_t kr = task_threads(thisTask, &threads, &threadCount);if (kr != KERN_SUCCESS) {return; }for (int i = 0; i < threadCount; i++) {thread_info_data_t threadInfo;thread_basic_info_t threadBaseInfo;mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) { threadBaseInfo = (thread_basic_info_t)threadInfo;if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {integer_t cpuUsage = threadBaseInfo->cpu_usage / 10;if (cpuUsage > CPUMONITORRATE) {//cup 消耗大于設置值時打印和記錄堆棧 NSString *reStr = smStackOfThread(threads[i]); SMCallStackModel *model = [[SMCallStackModel alloc] init]; model.stackStr = reStr;//記錄數據庫中 [[[SMLagDB shareInstance] increaseWithStackModel:model] subscribeNext:^(id x) {}];// NSLog(@'CPU useage overload thread stack:n%@',reStr);} }} }}卡頓方法的棧信息

當我們得到卡頓的時間點,就要立即拿到卡頓的堆棧,有兩種方式一種是遍歷棧幀,實現原理我在iOS獲取任意線程調用棧7寫的挺詳細的,同時開源了代碼RCBacktrace,另一種方式是通過Signal獲取任意線程調用棧,實現原理我在通過Signal handling(信號處理)獲取任意線程調用棧寫了,代碼在backtrace-swift,但這種方式在調試時比較麻煩,建議用第一種方式。

以上就是IOS中判斷卡頓的方案總結的詳細內容,更多關于IOS卡頓檢測的資料請關注好吧啦網其它相關文章!

標簽: IOS
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲在线久久| 亚洲午夜精品久久久久久app| 日韩影院二区| a天堂资源在线| 正在播放日韩精品| 99成人在线视频| 国产综合婷婷| 亚洲天堂黄色| 在线综合视频| 亚洲免费专区| 91精品视频一区二区| 国产精品一国产精品k频道56| 国产免费播放一区二区| 韩国女主播一区二区三区| 亚洲成a人片| 国产韩日影视精品| 巨乳诱惑日韩免费av| 亚洲啊v在线免费视频| 久久国内精品自在自线400部| 免费在线观看一区| 深夜视频一区二区| 亚洲女人av| 国产亚洲欧美日韩精品一区二区三区| 美女久久精品| 久久精品电影| 亚洲三区欧美一区国产二区| 欧美日一区二区三区在线观看国产免 | 亚洲欧美日本国产专区一区| 日欧美一区二区| 91伊人久久| 欧美激情久久久久久久久久久| 久久91导航| 亚洲精品无播放器在线播放| 国产精品白丝久久av网站 | 三上亚洲一区二区| 亚洲黄色在线| 青青在线精品| 亚洲www啪成人一区二区| 日韩视频一区| 麻豆免费精品视频| 亚洲激情国产| 你懂的国产精品| 午夜国产精品视频免费体验区| 三级欧美在线一区| 精品国产黄a∨片高清在线| 久久一区二区三区电影| 日韩在线成人| 亚洲不卡av不卡一区二区| 日韩成人精品一区二区三区 | 91嫩草亚洲精品| 久久国产精品99国产| 精品日产乱码久久久久久仙踪林| 亚洲午夜电影| 国产精品一区二区三区av麻| 91精品国产乱码久久久久久久| 日韩国产在线一| 99久久亚洲精品蜜臀| 欧美片网站免费| 国产综合婷婷| 精品日韩一区| 亚洲婷婷丁香| 日韩精品诱惑一区?区三区| 亚洲精品人人| 日韩精品免费一区二区在线观看| 欧美一区久久| 99国产精品99久久久久久粉嫩| 久久久免费人体| 色婷婷成人网| 激情五月色综合国产精品| 国产精品麻豆成人av电影艾秋| 黄色日韩在线| 日韩在线二区| 九九99久久精品在免费线bt| 日韩中文字幕在线一区| 久久精品国产99久久| 麻豆国产精品777777在线| 亚洲精品麻豆| 夜夜嗨一区二区| 神马日本精品| 精品视频在线你懂得| 日韩高清不卡一区二区| 乱人伦精品视频在线观看| 日韩不卡在线| 日韩av在线播放网址| 国产日韩欧美一区二区三区| 蜜桃久久av| 在线 亚洲欧美在线综合一区| 日韩黄色大片网站| 精品久久网站| 欧美激情aⅴ一区二区三区 | 久久精品青草| 国产精选在线| 精品一区二区三区中文字幕在线| 97精品久久| 亚洲人www| 国产精品日韩| 精品日韩毛片| 国产精品久久久久久久久久10秀 | 国产一区二区三区不卡视频网站| 青青草伊人久久| 综合一区在线| 丝袜亚洲精品中文字幕一区| 女人av一区| 欧美亚洲精品在线| 日韩欧美一区二区三区在线观看| 福利一区二区免费视频| 国产精品分类| 国产一级成人av| 日韩av中文在线观看| 亚洲精品美女| 亚洲精品影视| 日韩一区二区三区免费视频| 中文字幕日本一区二区| 免费久久精品视频| 亚洲一区二区三区四区电影| 蜜桃av一区二区| 亚洲色图综合| 少妇精品久久久| 日韩不卡免费视频| 欧美日韩夜夜| 久久99国产精品视频| 国产伊人久久| 日韩1区在线| 日韩伦理福利| 激情综合网址| 久久av在线| 四虎在线精品| 国产欧美日韩精品一区二区免费 | 国产一区二区三区不卡视频网站| 粉嫩av一区二区三区四区五区| 久久一区视频| 欧美日韩视频网站| 韩国一区二区三区视频| 人在线成免费视频| 婷婷综合网站| 国产精品婷婷| 亚洲人成精品久久久| 青青伊人久久| 欧美国产不卡| 久久精品青草| 免费日本视频一区| 欧美日一区二区三区在线观看国产免| 国产精品久久久亚洲一区| 粉嫩av一区二区三区四区五区| 高清一区二区三区| 99久久精品国产亚洲精品| 久久国产精品久久久久久电车| 亚洲网址在线观看| 国产精品欧美三级在线观看| 日韩免费视频| 蜜臀精品一区二区三区在线观看| 亚洲麻豆一区| 精品久久久网| 亚洲免费成人| 日本精品久久| 精品国产欧美| 午夜欧美精品久久久久久久| 日韩高清电影一区| 久久久久久一区二区| 香蕉久久国产| 老牛国内精品亚洲成av人片| 99久久婷婷| 综合五月婷婷| 国模大尺度视频一区二区| 婷婷综合在线| 国产精品日本一区二区不卡视频| 亚洲天堂一区二区| 日韩在线麻豆| 日韩精品1区| 一本一道久久a久久| 日韩av免费大片| 综合欧美精品| 桃色av一区二区| 日本在线视频一区二区| 国产精品xx| 日韩中文字幕1| 国内不卡的一区二区三区中文字幕| 久久久久蜜桃| 欧美日本不卡| 欧美日韩四区| 精品国产中文字幕第一页| 99视频在线精品国自产拍免费观看| 97久久精品| 婷婷成人基地| 久久精品国产亚洲aⅴ| 日韩中文字幕区一区有砖一区| 荡女精品导航| 日韩精品91亚洲二区在线观看| 日韩国产一区二区三区| 日本亚洲不卡| 亚洲午夜在线| 国产精品17p| 一本一本久久| 精品国产黄a∨片高清在线| 蜜臀va亚洲va欧美va天堂| 电影天堂国产精品| 国产精品极品在线观看| 国产精品免费看| 91精品韩国| 免费观看亚洲天堂|