zoukankan      html  css  js  c++  java
  • svg动画导致持续占用CPU

    1、在一次性能优化中突然发现一个svg矢量图动画导致CPU持续占用的问题,该svg在web中使用,

      即使webview释放之后,CPU依然占用达到10%,6s+上测试结果

      svg如下所示:

      

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" x="0px" y="0px" width="25px" height="25px" viewBox="0 0 25 25">
    
    <path  stroke="#777777" stroke-width="1" stroke-linejoin="round" stroke-linecap="round" fill="none" stroke-dasharray="64,6" stroke-dashoffset="0" d="
    M 3.4 5.65
    Q 9.5 2.2 11.2 1.15 12.6 0.4 14 1.3
    L 21.65 5.75
    Q 22.9 6.65 22.85 8.2
    L 22.85 17.4
    Q 22.8 18.6 21.5 19.25 14.85 23.25 13.6 23.9 12.3 24.55 10.8 23.7 4.3 19.9 3.1 19.1 2.35 18.6 2.15 17.75
    L 2.1 7.95
    Q 2.1 7.25 2.75 6.35
    L 3.4 5.65 Z">
    <animate attributeName="stroke-dashoffset" begin="0s" dur="1.5s" from="0" to="70" repeatCount="indefinite"/>
    </path>
    
    <path stroke="#777777" stroke-width="1" stroke-linejoin="round" stroke-linecap="round" fill="none" stroke-dasharray="37,4" stroke-dashoffset="41" d="
    M 7.15 8.35
    L 11.7 5.7
    Q 12.55 5.25 13.35 5.8
    L 17.85 8.4
    Q 18.6 8.95 18.55 9.85
    L 18.55 15.25
    Q 18.5 15.95 17.75 16.35 13.85 18.7 13.1 19.05 12.35 19.45 11.45 18.95
    L 6.95 16.25
    Q 6.55 16 6.4 15.45
    L 6.35 9.7
    Q 6.35 9.25 6.75 8.75
    L 7.15 8.35 Z">
    <animate attributeName="stroke-dashoffset" begin="0s" dur="1.5s" from="41" to="0" repeatCount="indefinite"/>
    </path>
    
    </svg>
    

      注意该动画的时间是无限长,指定时间结束之后,CPU将不再占用,因此这可能是webkit中的bug。

      svg是HTML5中的标准,每个webview都应该支持。未测试WKWebView兼容情况

    2、问题解决  

      可以看到该svg是由web中的css文件引用,真正的请求是:https://egame.gtimg.cn/club/pgg/v2.5/v2/img/global/loading-fecf2b8eea.svg

      为了可以将这个问题解决掉,在前端暂无法修复的情况下,我选择使用NSURLProtocol中的方法,hook该请求,并返回404

      具体操作使用了一个OHHTTPStubs的框架,代码如下:

      

    + (void)addLoadingSVG_Patch
    {
        [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
            
            NSString *url = [request.URL description];
            if([url hasSuffix:@"svg"] && [[url lastPathComponent] hasPrefix:@"loading"])
            {
                QG_Event(MODULE_LIVE_ASS, @"patch fuck loading animated svg request!!! = %@", request);
                return YES;
            }
            return NO;
        } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
            
            return [OHHTTPStubsResponse responseWithFileAtPath:@""
                                                    statusCode:404 headers:@{@"Content-Type":@"image/jpeg"}];
        }];
    }
    

      问题解决

    3、NSURLProtocol是苹果的一个协议,其中通过 + (BOOL)canInitWithRequest:(NSURLRequest *)request; 这个方法返回是否要手动接管这个请求

      通常web的离线缓存基于此实现,接管请求之后,需要自己实现请求的代码,并将返回结果通过NSURLProtocol的client对象回调给上层,例如:

      在startLoading方法中,实现请求,并返回结果。

    - (void)startLoading
    {
        self.clientRunLoop = CFRunLoopGetCurrent();
        NSURLRequest* request = self.request;
        id<NSURLProtocolClient> client = self.client;
        
        if (!self.stub)
        {
            NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                      @"It seems like the stub has been removed BEFORE the response had time to be sent.",
                                      NSLocalizedFailureReasonErrorKey,
                                      @"For more info, see https://github.com/AliSoftware/OHHTTPStubs/wiki/OHHTTPStubs-and-asynchronous-tests",
                                      NSLocalizedRecoverySuggestionErrorKey,
                                      request.URL, // Stop right here if request.URL is nil
                                      NSURLErrorFailingURLErrorKey,
                                      nil];
            NSError* error = [NSError errorWithDomain:@"OHHTTPStubs" code:500 userInfo:userInfo];
            [client URLProtocol:self didFailWithError:error];
            if (OHHTTPStubs.sharedInstance.afterStubFinishBlock)
            {
                OHHTTPStubs.sharedInstance.afterStubFinishBlock(request, self.stub, nil, error);
            }
            return;
        }
        
        OHHTTPStubsResponse* responseStub = self.stub.responseBlock(request);
        
        if (OHHTTPStubs.sharedInstance.onStubActivationBlock)
        {
            OHHTTPStubs.sharedInstance.onStubActivationBlock(request, self.stub, responseStub);
        }
        
        if (responseStub.error == nil)
        {
            NSHTTPURLResponse* urlResponse = [[NSHTTPURLResponse alloc] initWithURL:request.URL
                                                                         statusCode:responseStub.statusCode
                                                                        HTTPVersion:@"HTTP/1.1"
                                                                       headerFields:responseStub.httpHeaders];
            
            // Cookies handling
            if (request.HTTPShouldHandleCookies && request.URL)
            {
                NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseStub.httpHeaders forURL:request.URL];
                if (cookies)
                {
                    [NSHTTPCookieStorage.sharedHTTPCookieStorage setCookies:cookies forURL:request.URL mainDocumentURL:request.mainDocumentURL];
                }
            }
            
            
            NSString* redirectLocation = (responseStub.httpHeaders)[@"Location"];
            NSURL* redirectLocationURL;
            if (redirectLocation)
            {
                redirectLocationURL = [NSURL URLWithString:redirectLocation];
            }
            else
            {
                redirectLocationURL = nil;
            }
            [self executeOnClientRunLoopAfterDelay:responseStub.requestTime block:^{
                if (!self.stopped)
                {
                    // Notify if a redirection occurred
                    if (((responseStub.statusCode > 300) && (responseStub.statusCode < 400)) && redirectLocationURL)
                    {
                        NSURLRequest* redirectRequest = [NSURLRequest requestWithURL:redirectLocationURL];
                        [client URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:urlResponse];
                        if (OHHTTPStubs.sharedInstance.onStubRedirectBlock)
                        {
                            OHHTTPStubs.sharedInstance.onStubRedirectBlock(request, redirectRequest, self.stub, responseStub);
                        }
                    }
                    
                    // Send the response (even for redirections)
                    [client URLProtocol:self didReceiveResponse:urlResponse cacheStoragePolicy:NSURLCacheStorageNotAllowed];
                    if(responseStub.inputStream.streamStatus == NSStreamStatusNotOpen)
                    {
                        [responseStub.inputStream open];
                    }
                    [self streamDataForClient:client
                             withStubResponse:responseStub
                                   completion:^(NSError * error)
                     {
                         [responseStub.inputStream close];
                         NSError *blockError = nil;
                         if (error==nil)
                         {
                             [client URLProtocolDidFinishLoading:self];
                         }
                         else
                         {
                             [client URLProtocol:self didFailWithError:responseStub.error];
                             blockError = responseStub.error;
                         }
                         if (OHHTTPStubs.sharedInstance.afterStubFinishBlock)
                         {
                             OHHTTPStubs.sharedInstance.afterStubFinishBlock(request, self.stub, responseStub, blockError);
                         }
                     }];
                }
            }];
        } else {
            // Send the canned error
            [self executeOnClientRunLoopAfterDelay:responseStub.responseTime block:^{
                if (!self.stopped)
                {
                    [client URLProtocol:self didFailWithError:responseStub.error];
                    if (OHHTTPStubs.sharedInstance.afterStubFinishBlock)
                    {
                        OHHTTPStubs.sharedInstance.afterStubFinishBlock(request, self.stub, responseStub, responseStub.error);
                    }
                }
            }];
        }
    }
    
    - (void)stopLoading
    {
        self.stopped = YES;
    }
    

      

  • 相关阅读:
    获取文件当前目录及其大小
    PLC工作原理动图,一图搞懂一个原理
    欧拉角的详解
    欧拉角的详解
    PLC/Pragmas
    ASCII码对照表
    C++ 的关键字(保留字)完整介绍
    C++ 基本语法
    pytorch笔记1
    pytorchnum_flat_features(x)
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/6679953.html
Copyright © 2011-2022 走看看