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

长春哪里有做网站的信息流广告接单平台

长春哪里有做网站的,信息流广告接单平台,做爰全过程免费的视频99网站,wordpress ip 跳转去年年底做启动优化时,有个比较好玩的 case 给大家分享下,希望大家能从我的分享里 get 到我在做一些问题排查修复时是怎么看上去又low又土又高效的。 1. 现象 在我们使用 Perfetto 进行app 启动过程性能观测时,在 UI 线程发现了一段 几十毫…

去年年底做启动优化时,有个比较好玩的 case 给大家分享下,希望大家能从我的分享里 get 到我在做一些问题排查修复时是怎么看上去又low又土又高效的。

1. 现象

在我们使用 Perfetto 进行app 启动过程性能观测时,在 UI 线程发现了一段 几十毫秒接近百毫秒 的非预期Webview初始化的耗时(机器环境:小米10 pro),在线上用户机器上这段代码执行时间可能会更长。

为什么说非预期呢:

  • 首页没有WebView的使用、预加载
  • X5内核的初始化也在启动流程之后

2. 顺藤摸瓜

一般当我们发现了这种问题后,我们应该如何应对呢?

- 搞懂流程,如果在排查启动性能时,发现了不符合预期的主(子)线程耗时,第一步就是摸清楚这段耗时代码是怎么被调用的。

- 见招拆招,当我们知道代码如何被调用的之后,就可以想办法进行修复工作,如果是因为项目代码在错误的时机被调用,那就延后或者去除相关调用

WebViewChromiumAwInit.java

那我们开始第一步,搞懂流程,我们能看到图中耗时代码块被调用的系统方法是:

WebViewChromiumAwInit.startChromiumLocked,由于 Perfetto 并看不到 App 相关的堆栈信息,所以我们无法直接知道到底是哪行代码引起的。

那我们就去跟跟 webview 源码,看看具体情况,点进 WebViewChromiumAwInit.java 页面看相关代码,发现 startChromiumLocked 是被 ensureChromiumStartedLocked 方法调用的:

// This method is not private only because the downstream subclass needs to access it,
// it shouldn't be accessed from anywhere else.
/* package */ 
void ensureChromiumStartedLocked(boolean fromThreadSafeFunction) {assert Thread.holdsLock(mLock);if (mInitState == INIT_FINISHED) { // Early-out for the common case.return;}if (mInitState == INIT_NOT_STARTED) {// If we're the first thread to enter ensureChromiumStartedLocked, we need to determine// which thread will be the UI thread; declare init has started so that no other thread// will try to do this.mInitState = INIT_STARTED;setChromiumUiThreadLocked(fromThreadSafeFunction);}if (ThreadUtils.runningOnUiThread()) {// If we are currently running on the UI thread then we must do init now. If there was// already a task posted to the UI thread from another thread to do it, it will just// no-op when it runs.startChromiumLocked();return;}mIsPostedFromBackgroundThread = true;// If we're not running on the UI thread (because init was triggered by a thread-safe// function), post init to the UI thread, since init is *not* thread-safe.AwThreadUtils.postToUiThreadLooper(new Runnable() {@Overridepublic void run() {synchronized (mLock) {startChromiumLocked();}}});// Wait for the UI thread to finish init.while (mInitState != INIT_FINISHED) {try {mLock.wait();} catch (InterruptedException e) {// Keep trying; we can't abort init as WebView APIs do not declare that they throw// InterruptedException.}}}

那么 ensureChromiumStartedLocked 方法又是被谁调用的呢?我们在 WebViewChromiumAwInit.java 文件里大致找一下就能找到以下嫌疑人,第一反应是“这也太多了吧,这咋排查啊”

-getAwTracingController
-getAwProxyController
-startYourEngines
-getStatics
-getDefaultGeolocationPermissions
-getDefaultServiceWorkerController
-getWebIconDatabase
-getDefaultWebStorage
-getDefaultWebViewDatabasepublic class WebViewChromiumAwInit {public AwTracingController getAwTracingController() {synchronized (mLock) {if (mAwTracingController == null) {ensureChromiumStartedLocked(true);}}return mAwTracingController;}public AwProxyController getAwProxyController() {synchronized (mLock) {if (mAwProxyController == null) {ensureChromiumStartedLocked(true);}}return mAwProxyController;}void startYourEngines(boolean fromThreadSafeFunction) {synchronized (mLock) {ensureChromiumStartedLocked(fromThreadSafeFunction);}}public SharedStatics getStatics() {synchronized (mLock) {if (mSharedStatics == null) {ensureChromiumStartedLocked(true);}}return mSharedStatics;}public GeolocationPermissions getDefaultGeolocationPermissions() {synchronized (mLock) {if (mDefaultGeolocationPermissions == null) {ensureChromiumStartedLocked(true);}}return mDefaultGeolocationPermissions;}public AwServiceWorkerController getDefaultServiceWorkerController() {synchronized (mLock) {if (mDefaultServiceWorkerController == null) {ensureChromiumStartedLocked(true);}}return mDefaultServiceWorkerController;}public android.webkit.WebIconDatabase getWebIconDatabase() {synchronized (mLock) {ensureChromiumStartedLocked(true);if (mWebIconDatabase == null) {mWebIconDatabase = new WebIconDatabaseAdapter();}}return mWebIconDatabase;}public WebStorage getDefaultWebStorage() {synchronized (mLock) {if (mDefaultWebStorage == null) {ensureChromiumStartedLocked(true);}}return mDefaultWebStorage;}public WebViewDatabase getDefaultWebViewDatabase(final Context context) {synchronized (mLock) {ensureChromiumStartedLocked(true);if (mDefaultWebViewDatabase == null) {mDefaultWebViewDatabase = new WebViewDatabaseAdapter(mFactory,HttpAuthDatabase.newInstance(context, HTTP_AUTH_DATABASE_FILE),mDefaultBrowserContext);}}return mDefaultWebViewDatabase;}
}

WebViewChromiumFactoryProvider.java

经过上面对的简单分析,我们大概知道了WebViewChromiumAwInit.startChromiumLocked是被 ensureChromiumStartedLocked 方法调用的,而ensureChromiumStartedLocked 方法会被以下几个方法调用,那我们接下来的工作就需要找到下面这几个方法到底被谁调用了。

-getAwTracingController
-getAwProxyController
-startYourEngines
-getStatics
-getDefaultGeolocationPermissions
-getDefaultServiceWorkerController
-getWebIconDatabase
-getDefaultWebStorage
-getDefaultWebViewDatabase

到这里,分享我的一个土方法,我们要找到底哪个地方会调用这些方法,那就找一个不认识的、看上去不会被别人提起的方法,进行 google,我们一眼就选中了getDefaultServiceWorkerController 方法,没办法,谁叫我不认识你呢。虽然方法笨,但是架不住效率啊。于是乎,我们把它揪出来了 - WebViewChromiumFactoryProvider.java

我们大概了解一下 WebViewChromiumFactoryProvider 大概是个什么角色,WebViewChromiumFactoryProvider 实现了 WebViewFactoryProvider 接口,简单理解就是 WebView 的工厂,App 如果要创建 WebView,就会通过 WebViewFactoryProvider 接口的实现类进行 createWebView,所以其实就是个工厂模式。通过抽象规范 api,保证兼容性和可移植性可扩展性。

我们在这个文件中也如愿以偿的看到了上面列出来的几个方法的调用。WebViewChromiumFactoryProvider 在接口方法的实现中,调用了WebViewChromiumAwInit 里的一系列方法,如下:

//WebViewChromiumFactoryProvider.java
@Override
public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking);
}//我们截取一段@Overridepublic GeolocationPermissions getGeolocationPermissions() {return mAwInit.getDefaultGeolocationPermissions();}@Overridepublic CookieManager getCookieManager() {return mAwInit.getDefaultCookieManager();}@Overridepublic ServiceWorkerController getServiceWorkerController() {synchronized (mAwInit.getLock()) {if (mServiceWorkerController == null) {mServiceWorkerController = new ServiceWorkerControllerAdapter(mAwInit.getDefaultServiceWorkerController());}}return mServiceWorkerController;}@Overridepublic TokenBindingService getTokenBindingService() {return null;}@Overridepublic android.webkit.WebIconDatabase getWebIconDatabase() {return mAwInit.getWebIconDatabase();}@Overridepublic WebStorage getWebStorage() {return mAwInit.getDefaultWebStorage();}@Overridepublic WebViewDatabase getWebViewDatabase(final Context context) {return mAwInit.getDefaultWebViewDatabase(context);}WebViewDelegate getWebViewDelegate() {return mWebViewDelegate;}WebViewContentsClientAdapter createWebViewContentsClientAdapter(WebView webView,Context context) {try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.insideCreateWebViewContentsClientAdapter")) {return new WebViewContentsClientAdapter(webView, context, mWebViewDelegate);}}void startYourEngines(boolean onMainThread) {try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.startYourEngines")) {mAwInit.startYourEngines(onMainThread);}}boolean hasStarted() {return mAwInit.hasStarted();}

3. 确定问题

我们上面通过阅读 WebViewChromiumFactoryProvider.javaWebViewChromiumAwInit.java 这两个文件具体代码实现,有了一个比较清晰的思路。

App 在初始化过程中,调用到了 WebViewFactoryProvider 接口实现类的某个方法,这个方法调用了 WebViewChromiumAwInit 的下面方法中的其中一个或者多个。那其实问题就清晰了,我们只需要找到,我们 app 启动阶段到底哪行代码,会调用到 WebViewFactoryProvider 接口某个接口方法就行。

-getAwTracingController
-getAwProxyController
-startYourEngines
-getStatics
-getDefaultGeolocationPermissions
-getDefaultServiceWorkerController
-getWebIconDatabase
-getDefaultWebStorage
-getDefaultWebViewDatabase

由于 WebView 的代码并不会打包进 app 里,App 用的 WebView 内核都是用的 Android 系统负责内置、升级的 WebView 内核代码,所以通过 transform 的方式进行 hook 调用是不行的,这里我们采用动态代理的方式,对 WebViewFactoryProvider 接口方法进行 hook,我们通过动态代理生成一个 proxy 对象,通过反射的方式,替换掉 android.webkit.WebViewFactorysProviderInstance 对象。

    ##WebViewFactory@SystemApipublic final class WebViewFactory{//...@UnsupportedAppUsageprivate static WebViewFactoryProvider sProviderInstance;//...}##动态代理try {Class clas = Class.forName("android.webkit.WebViewFactory");Method method = clas.getDeclaredMethod("getProvider");method.setAccessible(true);Object obj = method.invoke(null);Object hookService = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getSuperclass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Log.d("zttt", "hookService method: " + method.getName());new RuntimeException(method.getName()).printStackTrace();return method.invoke(obj, args);}});Field field = clas.getDeclaredField("sProviderInstance");field.setAccessible(true);field.set(null, hookService);} catch (Exception e) {e.printStackTrace();}

替换掉 sProviderInstance 之后,我们就可以在我们的代理逻辑中,加上断点来进行调试,最终找到了造成 WebView非预期初始化的始作俑者:WebSettings.getDefaultUserAgent

4. 解决问题

事情到这里就好解决了,只需要对 WebSettings.getDefaultUserAgent 进行编译期的Hook,重定向到带缓存defaultUserAgent 的相关方法就行了,本地有缓存则直接读取,本地没有则立即读取,得益于之前我在项目中实现的使用方便的 配置化 Hook 框架,这种小打小闹的 Hook 工作不到一分钟就能完成。

当然,这里还需要考虑一个问题,那就是当用户机器的 defaultUserAgent 发生变化之后,怎么才能及时的更新本地缓存以及网络请求中用上新的defaultUserAgent。我们的做法是:

  • 当本地没有缓存时,立刻调用 WebSettings.getDefaultUserAgent 拿值并更新缓存;

  • 每次App启动阶段结束之后,会在子线程中去调用WebSettings.getDefaultUserAgent 拿值并更新缓存。

这样处理之后,将 defaultUserAgent 发生变化之后的影响最小化,系统 WebView 升级本身就是极度不频繁的事情,在这种 case 下我们舍弃了下一次 App 打开前几个网络请求的 defaultUserAgent 正确性也是合理的,这也是我们考量 「风险收益比」的一个经典case。

5. 确认问题被解决

通过上述 hook,我们重新打包 run 一遍 app,在启动阶段已经观察不到相关耗时了。

搞定,收工,不仅解决问题效率高,写博客也效率高,一会就整完了,简直就像是季度绩效考核前的产品,出方案和上线的效率就一个字,嗖。

为了帮助到大家更好的全面清晰的掌握好性能优化,准备了相关的核心笔记(还含底层逻辑):https://qr18.cn/FVlo89

性能优化核心笔记:https://qr18.cn/FVlo89

启动优化

内存优化

UI优化

网络优化

Bitmap优化与图片压缩优化https://qr18.cn/FVlo89

多线程并发优化与数据传输效率优化

体积包优化

《Android 性能监控框架》:https://qr18.cn/FVlo89

《Android Framework学习手册》:https://qr18.cn/AQpN4J

  1. 开机Init 进程
  2. 开机启动 Zygote 进程
  3. 开机启动 SystemServer 进程
  4. Binder 驱动
  5. AMS 的启动过程
  6. PMS 的启动过程
  7. Launcher 的启动过程
  8. Android 四大组件
  9. Android 系统服务 - Input 事件的分发过程
  10. Android 底层渲染 - 屏幕刷新机制源码分析
  11. Android 源码分析实战

http://www.hkea.cn/news/773602/

相关文章:

  • 注册网站免费注册insseo关键词优化推广哪家好
  • 深圳房地产网站开发常见的网络营销工具有哪些
  • .net 网站管理系统湖南企业竞价优化首选
  • 南山区住房与建设局官方网站网络赚钱推广
  • wordpress mycred汉化seo引擎搜索入口
  • 在线教育网站用什么做百度搜索的优势
  • 甘肃省住房城乡建设厅网站首页智能建站模板
  • 智能科技网站模板下载地址百度学术论文查重
  • 网站要怎么做才能让360收录推广品牌的策划方案
  • 做网站前景营销课程培训视频
  • 青海做网站广告开户南京seo
  • wordpress写软文赚钱seo快速培训
  • 南宁网站建设接单陕西省人民政府
  • wordpress网站价格seo域名综合查询
  • 支付网站怎么做的网络自动推广软件
  • js做网站统计品牌关键词优化
  • 微信公众号管理平台官网谷歌seo建站
  • 鲜花购物网站源码企业网站营销的优缺点
  • 表白网站制作在线日照网站优化公司
  • 企业网站建设策划书 前言徐州关键词优化排名
  • 一级a做爰片视频网站全国新闻媒体发稿平台
  • 唐山网站建设哪家专业高德北斗导航
  • wordpress 地址 .html企业网站seo贵不贵
  • 提供网站制作公司哪家好网络软文范文
  • 做原型网站枣庄网络推广seo
  • 品牌网站开发设计外贸网站平台
  • 网站做留言板网站推广在线
  • 长春服务好的网络营销seo网站推广的主要目的
  • 搜索引擎优化和关键词竞价广告的区别宿州百度seo排名软件
  • 一搜同志网站建设电话青岛网站seo优化