Просмотр исходного кода

perf(reader): baseline profile 补采开书热路径,消除首开 JIT 卡顿

BaselineProfileGenerator 的 journey 之前只在书架上下滑动、从不开书,导致
Compose 阅读器迁移后,开书热路径从未被采样进 baseline profile:release 包
里 ReadBookRouteScreen / ReadBookScreen / ChapterProvider /
TextChapterLayout.getTextChapter / ReadView 等热类没有 AOT,首次开书只能在
主线程内联 JIT,实测卡顿约 600~800ms(热态开书仅 ~250ms)。旧 profile 还
引用了早已删除的 ReadBookActivity,属迁移前遗留。

journey 增加"打开一本书进入阅读器 + 翻页 + 返回",并重新生成 profile:
- 开书前不再滑动书架(下滑会展开顶栏、挤走书目导致点不中)
- 书目是 Compose 语义节点(uiautomator 中 clickable=false),用书特有的
  进度描述定位、取 bounds 中心原始坐标点击
- 阅读器热类方法覆盖从 ~0 升至数百条,getTextChapter 现标记为 HPL(AOT)
5151561 3 месяцев назад
Родитель
Сommit
2fcd4248be

Разница между файлами не показана из-за своего большого размера
+ 8078 - 5645
app/src/appRelease/generated/baselineProfiles/baseline-prof.txt


Разница между файлами не показана из-за своего большого размера
+ 8078 - 5645
app/src/appRelease/generated/baselineProfiles/startup-prof.txt


+ 40 - 26
baselineProfile/src/main/java/io/legado/baselineprofile/BaselineProfileGenerator.kt

@@ -5,15 +5,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.By
-import androidx.test.uiautomator.Direction
 import androidx.test.uiautomator.Until
 import androidx.test.uiautomator.Until
 import org.junit.Rule
 import org.junit.Rule
 import org.junit.Test
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runner.RunWith
+import java.util.regex.Pattern
 
 
 /**
 /**
- * This test class generates a basic startup baseline profile for the target package.
- **/
+ * 生成 baseline profile。
+ *
+ * 关键:journey 必须真正打开一本书进入阅读器,否则开书热路径(Compose 组合、
+ * ChapterProvider/TextChapterLayout 排版、ReadView 绘制)不会被采样进 profile,
+ * release 首次开书只能主线程内联 JIT,产生明显加载卡顿。
+ *
+ * 运行前提:目标设备书架里至少有一本书(本仓库用连接的真机生成,见 useConnectedDevices)。
+ */
 @RunWith(AndroidJUnit4::class)
 @RunWith(AndroidJUnit4::class)
 @LargeTest
 @LargeTest
 class BaselineProfileGenerator {
 class BaselineProfileGenerator {
@@ -33,38 +39,46 @@ class BaselineProfileGenerator {
             pressHome()
             pressHome()
             startActivityAndWait()
             startActivityAndWait()
 
 
+            // 等书架加载完成。
             val bookshelfSelector = By.desc("bookshelf_list")
             val bookshelfSelector = By.desc("bookshelf_list")
             device.wait(Until.hasObject(bookshelfSelector), 10000)
             device.wait(Until.hasObject(bookshelfSelector), 10000)
-
             device.waitForIdle()
             device.waitForIdle()
             Thread.sleep(2000)
             Thread.sleep(2000)
 
 
-            val screenWidth = device.displayWidth
-            val screenHeight = device.displayHeight
-            val startX = screenWidth / 2
-            val startY = (screenHeight * 0.75).toInt()
-            val endY = (screenHeight * 0.25).toInt()
+            val w = device.displayWidth
+            val h = device.displayHeight
 
 
-            repeat(3) {
-                device.swipe(startX, startY, startX, endY, 25)
-                Thread.sleep(1000)
+            // 打开第一本书进入阅读器。
+            // 注意:开书前不要滑动书架——下滑会让顶栏展开、把书目挤下去,位置漂移点不中。
+            // 书目是 Compose 语义节点,uiautomator 中 clickable=false,UiObject2.click() 不可靠,
+            // 因此用「书特有的进度描述」(未读/已读/第N章)精确定位书节点,取其 bounds 中心,
+            // 用原始坐标点击(等价 input tap,最稳)。找不到时退回已验证的左上角书位坐标。
+            val bookMatcher = By.desc(Pattern.compile(".*(未读|已读|读到|第.{1,8}章).*"))
+            val bookNode = device.wait(Until.findObject(bookMatcher), 5000)
+            if (bookNode != null) {
+                val b = bookNode.visibleBounds
+                device.click(b.centerX(), b.centerY())
+            } else {
+                device.click((w * 0.18f).toInt(), (h * 0.37f).toInt())
             }
             }
+            device.waitForIdle()
+            Thread.sleep(3000)
+
+            // 若点书进的是书籍详情页,点“阅读”FAB 进入阅读器;已直接进阅读器则为 null,跳过。
+            device.wait(Until.findObject(By.text("阅读")), 2000)?.click()
+            device.waitForIdle()
+
+            // 等阅读器首屏排版完成。
+            Thread.sleep(3500)
 
 
-            repeat(2) {
-                device.swipe(startX, endY, startX, startY, 10)
-                Thread.sleep(1000)
+            // 翻几页,顺带采样相邻章排版与翻页绘制。
+            repeat(4) {
+                device.swipe((w * 0.85f).toInt(), h / 2, (w * 0.15f).toInt(), h / 2, 8)
+                Thread.sleep(900)
             }
             }
 
 
-            // "nav_my"
-            /* val tabs = listOf("nav_explore", "nav_rss", "nav_bookshelf")
-            for (tabDesc in tabs) {
-                val tab = device.wait(Until.findObject(By.desc(tabDesc)), 5000)
-                if (tab != null) {
-                    val bounds = tab.visibleBounds
-                    device.click(bounds.centerX(), bounds.centerY())
-                    Thread.sleep(3000)
-                }
-            }*/
+            device.pressBack()
+            Thread.sleep(1500)
         }
         }
     }
     }
-}
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов