From 0037aefcb723404566e017e49a39ff80eca612e8 Mon Sep 17 00:00:00 2001 From: 7015725 Date: Tue, 12 May 2026 10:00:32 +0800 Subject: [PATCH] fix: improve ToolHub startup and cleanup stability --- code/th_14_panels.js | 81 ++---------------------------- code/th_16_entry.js | 117 +++++++++++++++++++++++++++---------------- manifest.json | 10 ++-- manifest.sig | 2 +- 4 files changed, 86 insertions(+), 124 deletions(-) diff --git a/code/th_14_panels.js b/code/th_14_panels.js index eacba13..d6b9f2c 100644 --- a/code/th_14_panels.js +++ b/code/th_14_panels.js @@ -2311,78 +2311,7 @@ var scInlineState = { lastQuery: "" }; -// # 图标缓存与队列(避免每次重渲染都重复取 icon,减少卡顿) -// 这段代码的主要内容/用途:为内联列表提供轻量级 icon 缓存与串行加载队列,避免一次性开太多线程。 -var scIconCache = {}; -var scIconQueue = []; -var scIconWorkerRunning = false; -function __scIconKey(it) { - try { return __scStr(it.pkg) + '|' + __scStr(it.shortcutId) + '|' + __scStr(it.userId); } catch(e) { return ''; } -} -function __scLoadIconForItem(it) { - // 这段代码的主要内容/用途:优先取快捷方式图标,失败则回退到应用图标。 - try { - if (!it) return null; - if (it.shortcutInfo) { - try { - var la = context.getSystemService(android.content.Context.LAUNCHER_APPS_SERVICE); - if (la) { - var dr = la.getShortcutIconDrawable(it.shortcutInfo, 0); - if (dr) return dr; - } - } catch(eS0) { safeLog(null, 'e', "catch " + String(eS0)); } - } - try { - var pm = context.getPackageManager(); - return pm.getApplicationIcon(__scStr(it.pkg)); - } catch(eA0) { safeLog(null, 'e', "catch " + String(eA0)); } - } catch(eAll0) { safeLog(null, 'e', "catch " + String(eAll0)); } - return null; -} -function __scEnqueueIconLoad(it, iv) { - try { - var key = __scIconKey(it); - if (!key) return; - if (scIconCache[key]) { - try { iv.setImageDrawable(scIconCache[key]); } catch(eSet0) { safeLog(null, 'e', "catch " + String(eSet0)); } - return; - } - // # 记录 tag:防止滚动/重绘后错位 - try { iv.setTag(key); } catch(eTag0) { safeLog(null, 'e', "catch " + String(eTag0)); } - scIconQueue.push({ key: key, it: it, iv: iv }); - if (!scIconWorkerRunning) { - scIconWorkerRunning = true; - new java.lang.Thread(new java.lang.Runnable({ - run: function() { - while (true) { - var job = null; - try { if (scIconQueue.length > 0) job = scIconQueue.shift(); } catch(eQ0) { job = null; } - if (!job) break; - - var dr = null; - try { dr = __scLoadIconForItem(job.it); } catch(eLd0) { dr = null; } - if (dr) scIconCache[job.key] = dr; - - try { - self.runOnUiThreadSafe(function() { - try { - if (!job || !job.iv) return; - var cur = null; - try { cur = job.iv.getTag(); } catch(eTg0) { cur = null; } - if (cur && String(cur) === String(job.key) && dr) { - job.iv.setImageDrawable(dr); - } - } catch(eUi0) { safeLog(null, 'e', "catch " + String(eUi0)); } - }); - } catch(ePost0) { safeLog(null, 'e', "catch " + String(ePost0)); } - } - scIconWorkerRunning = false; - } - })).start(); - } - } catch(eEnq0) { safeLog(null, 'e', "catch " + String(eEnq0)); } -} - +// # 图标缓存与加载已统一使用下方 __scIconLoader / __scRequestIcon,避免维护两套后台线程队列。 // # 折叠头部(点击展开/收起) var scHeader = new android.widget.LinearLayout(context); scHeader.setOrientation(android.widget.LinearLayout.HORIZONTAL); @@ -2410,10 +2339,10 @@ scRefreshTv.setOnClickListener(new android.view.View.OnClickListener({ try { scInlineState.forceReload = true; scInlineState.loaded = false; - // 清空 icon 缓存,避免旧图标占用内存且影响新列表显示 - try { scIconCache = {}; } catch(eC0) { safeLog(null, 'e', "catch " + String(eC0)); } - try { scIconQueue = []; } catch(eC1) { safeLog(null, 'e', "catch " + String(eC1)); } - try { scIconWorkerRunning = false; } catch(eC2) { safeLog(null, 'e', "catch " + String(eC2)); } + // 清空当前内联列表的图标缓存,避免旧图标占用内存且影响新列表显示 + try { __scIconCache = {}; } catch(eC0) { safeLog(null, 'e', "catch " + String(eC0)); } + try { __scIconKeys = []; } catch(eC1) { safeLog(null, 'e', "catch " + String(eC1)); } + try { __scIconInFlight = {}; } catch(eC2) { safeLog(null, 'e', "catch " + String(eC2)); } // 若当前已展开,立即触发重新加载与渲染 if (scInlineState.expanded) __scEnsureLoadedAndRender(); } catch(eR) { safeLog(null, 'e', "catch " + String(eR)); } diff --git a/code/th_16_entry.js b/code/th_16_entry.js index 1751359..2918a22 100644 --- a/code/th_16_entry.js +++ b/code/th_16_entry.js @@ -119,12 +119,12 @@ FloatBallAppWM.prototype.close = function() { } } catch (eIcon) {} try { - if (self.__scIconLoaderSingleton && self.__scIconLoaderSingleton.ht) { - if (android.os.Build.VERSION.SDK_INT >= 18) self.__scIconLoaderSingleton.ht.quitSafely(); - else self.__scIconLoaderSingleton.ht.quit(); + if (this.__scIconLoaderSingleton && this.__scIconLoaderSingleton.ht) { + if (android.os.Build.VERSION.SDK_INT >= 18) this.__scIconLoaderSingleton.ht.quitSafely(); + else this.__scIconLoaderSingleton.ht.quit(); } } catch (eScIcon) {} - try { self.__scIconLoaderSingleton = null; } catch (eScIcon2) {} + try { this.__scIconLoaderSingleton = null; } catch (eScIcon2) {} safeLog(this.L, 'i', "close done"); @@ -158,8 +158,8 @@ FloatBallAppWM.prototype.dispose = function() { // # 清理单例引用 try { - if (self.__shortcutPickerSingleton === this.__shortcutPickerSingleton) { - self.__shortcutPickerSingleton = null; + if (this.__shortcutPickerSingleton) { + this.__shortcutPickerSingleton = null; } } catch (e) {} @@ -245,50 +245,83 @@ FloatBallAppWM.prototype.startAsync = function(entryProcInfo, closeRule) { ); if (cfgRcv) this.state.receivers.push(cfgRcv); - h.post(new JavaAdapter(java.lang.Runnable, { - run: function() { - try { - self.state.wm = context.getSystemService(android.content.Context.WINDOW_SERVICE); - self.state.density = context.getResources().getDisplayMetrics().density; - - if (self.L) self.L.updateConfig(self.config); - - self.state.screen = self.getScreenSizePx(); - self.state.lastRotation = self.getRotation(); - self.state.loadedPos = self.loadSavedPos(); - - self.createBallViews(); - self.state.ballLp = self.createBallLayoutParams(); - + var startBox = { ok: false, err: "启动确认超时", added: false }; + var startLatch = new java.util.concurrent.CountDownLatch(1); + var posted = false; + try { + posted = h.post(new JavaAdapter(java.lang.Runnable, { + run: function() { try { - self.state.wm.addView(self.state.ballRoot, self.state.ballLp); - self.state.addedBall = true; - } catch (eAdd) { - try { self.toast("悬浮球 addView 失败: " + String(eAdd)); } catch (eT) {} - if (self.L) self.L.fatal("addView ball fail err=" + String(eAdd)); - self.state.addedBall = false; - try { self.close(); } catch (eC) {} - return; - } + self.state.wm = context.getSystemService(android.content.Context.WINDOW_SERVICE); + self.state.density = context.getResources().getDisplayMetrics().density; - self.setupDisplayMonitor(); - self.touchActivity(); + if (self.L) self.L.updateConfig(self.config); - if (self.L) { - self.L.i("start ok actionClose=" + String(self.config.ACTION_CLOSE_ALL)); - self.L.i("ball x=" + String(self.state.ballLp.x) + " y=" + String(self.state.ballLp.y) + " sizeDp=" + String(self.config.BALL_SIZE_DP)); + self.state.screen = self.getScreenSizePx(); + self.state.lastRotation = self.getRotation(); + self.state.loadedPos = self.loadSavedPos(); + + self.createBallViews(); + self.state.ballLp = self.createBallLayoutParams(); + + try { + self.state.wm.addView(self.state.ballRoot, self.state.ballLp); + self.state.addedBall = true; + startBox.added = true; + } catch (eAdd) { + startBox.ok = false; + startBox.err = "悬浮球 addView 失败: " + String(eAdd); + try { self.toast(startBox.err); } catch (eT) {} + if (self.L) self.L.fatal("addView ball fail err=" + String(eAdd)); + self.state.addedBall = false; + try { self.close(); } catch (eC) {} + return; + } + + self.setupDisplayMonitor(); + self.touchActivity(); + + startBox.ok = true; + startBox.err = ""; + if (self.L) { + self.L.i("start ok actionClose=" + String(self.config.ACTION_CLOSE_ALL)); + self.L.i("ball x=" + String(self.state.ballLp.x) + " y=" + String(self.state.ballLp.y) + " sizeDp=" + String(self.config.BALL_SIZE_DP)); + } + } catch (eAll) { + startBox.ok = false; + startBox.err = "启动异常: " + String(eAll); + try { self.toast(startBox.err); } catch (eTT2) {} + if (self.L) self.L.fatal("start runnable err=" + String(eAll)); + try { self.close(); } catch (eC2) {} + } finally { + try { startLatch.countDown(); } catch (eLatch) {} } - } catch (eAll) { - try { self.toast("启动异常: " + String(eAll)); } catch (eTT2) {} - if (self.L) self.L.fatal("start runnable err=" + String(eAll)); - try { self.close(); } catch (eC2) {} } + })); + } catch (ePost) { + posted = false; + startBox.ok = false; + startBox.err = "启动任务投递失败: " + String(ePost); + try { startLatch.countDown(); } catch (eLatch2) {} + } + + if (!posted) { + startBox.ok = false; + if (!startBox.err) startBox.err = "启动任务投递失败"; + } else { + try { + var done = startLatch.await(2500, java.util.concurrent.TimeUnit.MILLISECONDS); + if (!done && self.L) self.L.e("start confirm timeout; addView result unknown"); + } catch (eWait) { + startBox.ok = false; + startBox.err = "启动确认等待异常: " + String(eWait); } - })); + } return { - ok: true, - msg: "已按 WM 专属 HandlerThread 模型启动(Shell 默认 Action,失败广播桥兜底;Content URI 已启用)", + ok: !!startBox.ok, + err: String(startBox.err || ""), + msg: startBox.ok ? "悬浮球 addView 已确认成功" : String(startBox.err || "启动失败"), preCloseBroadcastSent: preCloseSent, closeAction: String(this.config.ACTION_CLOSE_ALL), receiverRegisteredOnMain: { diff --git a/manifest.json b/manifest.json index b1012ec..256ce3c 100644 --- a/manifest.json +++ b/manifest.json @@ -54,19 +54,19 @@ "size": 20386 }, "th_14_panels.js": { - "sha256": "65528739877540cdd457017ebd083e1ad0f1f911706129d75a88647c48a59c51", - "size": 220509 + "sha256": "a237b164e29f4a581893b86bd7d3f9776dc0ecc7a56ecc53f9465525fa117253", + "size": 217295 }, "th_15_extra.js": { "sha256": "c8e10fe54a965fe7b27f0996b2f2636655077e39df774d6cbb1e73c5b36553fc", "size": 62625 }, "th_16_entry.js": { - "sha256": "8c7d2d8dfa1dc51b47a01be8639a9da78cb40670c66cc8f78d339afecfd83be5", - "size": 11125 + "sha256": "6bb08673d720665e2d6a04d4b2021cc7f836818f4781daff59cb7b382322d1c9", + "size": 12285 } }, "keyId": "toolhub-targets-2026-rsa3072", "schema": 2, - "version": 20260507155220 + "version": 20260512020008 } diff --git a/manifest.sig b/manifest.sig index 63e1111..a39360c 100644 --- a/manifest.sig +++ b/manifest.sig @@ -1 +1 @@ -TwIGuNRj9b0pIqiICJpA3vNxwNtQfsSj4LQKyOcUfuVxbbDsAJ1SZWra9ioDdd1jdjo8jEiSHMZ5JgDdKSkrUQZwUpQYsBXdtFFS2Zb5aoGk7QLgrgHRoJvvJOinU3/DLS93by3msNMoEJmZLQEoa4ltXK7hehc5dclp82ZOXg3p5A1OCgdzavGzSqPfg4eH1o3GHzS2I/zDhZgzrbKNcoKUsy//fr5E34ePQ4aWNXAmcyQFNXLT8aCDL+yXn3wjbTgASuHS+GncwbYJ5y4/toMyW+EJNX0xiOsIa7OFA7jRfX0U6qqgZH0fW/jGlZNeJhnqZYfOpjKIPy7t7B5iThJT0iiJ6L4X/mjUe9ggO/YAhJ7g4Vp8m/g5LpmWpVPLRwYJje6Q5Ypulm95CaBek2FEjXwqGa1hsCQ1MfMj1rCwfn7biLH1Vkfff+JgoRvE/2JWbPMLU+j1jCNOLWVUPIpTsazu8/kR4NkjJPUm7uZX5ogtfRhGbkLl08+lB8Lm +axGRqLze6TvsjEgpjNjRGL0u2CHxBCSwWZ8AKQBZ2Q9Gr8rMajyTkNt6vRW0aHPWaJhM9Silyh/3VIoy4iz0Id2RltAwADV7XygLGL2jMGlHsjC2U1g8bavgL1JKK/UY0WvnQ1fUgI2bDqWA2eN/SQvP0/DbRwOeheCvcNrf5aHxMuEYyorIbbhFxsqGXb8Uut6ieKe2aUV5jSL1LOatt2QKgnOM5ylpRmf3M9w3g8f4SDjHKhCup6K7H52ooazoFmMP030suXVhn1vmujrZtQtcgmzMU6hI1ZSZOcPIekgWxzIlJ2ktXxR/b9TlQ4JcEtxG2D/jIx02M0mQXE9kfZZJ7I3g6h2FNc4YdJS51pXr7YvtGPevR6yxP+/J5e6fVcm+W5EW8eFHzrtyhBI8zvG7vJSCYBV+jwl/XNznwnx0whduQ7RqZMM4++xdzyew5wtIWnlVxI4wqX+B852cnSU/EdTPu2HoBt+TfqEO5p7oDok1pO01i5BWghkvujKt