:SimpleUI如何安全修改KOReader類并可靠還原)
【免費(fèi)下載鏈接】simpleui.kopluginA highly customizable UI plugin for KOReader that features a home screen, bottom navigation bar, top bar and desktop modules/widgets.項(xiàng)目地址https://gitcode.com/gh_mirrors/si/simpleui.koplugin點(diǎn)擊查看免費(fèi)下載SimpleUI 是一款面向KOReader的高度可定制 UI 插件它為電子書閱讀器帶來主頁屏、底部導(dǎo)航欄、頂部狀態(tài)欄和豐富的桌面模塊。它的強(qiáng)大體驗(yàn)靠的正是infra/sui_patches.lua5673 行中一套精心的monkey-patch機(jī)制——在不改動(dòng) KOReader 源碼的前提下劫持 并增強(qiáng)核心類的方法再在卸載時(shí)逐一可靠還原。本文帶你精讀這套鉤子體系看懂一個(gè)插件如何做到改得進(jìn)去、收得回來。為什么插件需要 Monkey-PatchKOReader 是開源閱讀器插件無法直接修改它的源代碼。SimpleUI 想要在文件管理器下方加一條導(dǎo)航欄、調(diào)整布局尺寸讓返回鍵 / Home 鍵直達(dá)主頁屏給全屏菜單注入壁紙背景、縮小高度以避開導(dǎo)航欄刪除書籍前先把已讀完的書記入統(tǒng)計(jì)。這些需求都落在 KOReader 的核心類上比如FileManager、UIManager、Menu。Monkey-Patch 的思路很直白把原方法保存起來再替換成先做自己的事再調(diào)用原方法的新函數(shù)。關(guān)鍵在于——替換必須可逆否則關(guān)掉插件后殘留的鉤子會(huì)持續(xù)干擾原行為。核心設(shè)計(jì)一套可復(fù)用的可逆鉤子SimpleUI 沒有散落地orig Foo.bar; Foo.bar ...而是抽象出四個(gè)小函數(shù)統(tǒng)一管理全部集中在 infra/sui_patches.lua函數(shù)作用_acquireHooks(target, key, owner)登記使用者。若鉤子已存在返回nil保證同一方法永不被包兩次_addHook(state, target, key, wrapped)用rawget取出原函數(shù)存入state.hooks再替換target[key]_trackHooks(state, entries)為已被賦值過的函數(shù)補(bǔ)登記方便后續(xù)移除_releaseHooks(target, key, owner)注銷使用者當(dāng)最后一個(gè)使用者離開時(shí)才真正還原且按逆序恢復(fù)還原時(shí)的逆序循環(huán)是整個(gè)設(shè)計(jì)的精髓infra/sui_patches.luafor i #state.hooks, 1, -1 do local h state.hooks[i] if rawget(h.target, h.key) h.wrapped then h.target[h.key] h.orig end end這里有個(gè)關(guān)鍵細(xì)節(jié)rawget(h.target, h.key) h.wrapped先確認(rèn)現(xiàn)在掛在方法上的確實(shí)是我自己包的這一層才去還原。這樣即使別處又在它之上包了一層SimpleUI 也只會(huì)拆掉自己的那一層不會(huì)誤傷他人——這是它能和別的 user-patch 共存的基礎(chǔ)。為什么狀態(tài)要掛在類上而不是插件實(shí)例上因?yàn)?KOReader 每次打開/關(guān)閉書籍都會(huì)重建插件實(shí)例而類表在整個(gè)會(huì)話中是穩(wěn)定的。掛在類上鉤子才能跨越實(shí)例重建繼續(xù)生效。七大鐵律讓鉤子不怕重復(fù)安裝KOReader 會(huì)為每個(gè)宿主界面文件管理器、閱讀器各創(chuàng)建一個(gè)插件實(shí)例并在每次開書/關(guān)書時(shí)反復(fù)創(chuàng)建。SimpleUI 的installAll/teardownAll因此會(huì)被反復(fù)觸發(fā)且順序不受插件控制。為此作者在文件頭部寫明了 7 條規(guī)則一律通過_acquireHooks安裝——已存在則返回nil函數(shù)絕不會(huì)被包兩次每個(gè)包裝都要登記_addHook或_trackHooks以便移除在teardownAll中通過_releaseHooks釋放——只有最后一個(gè)使用者離開時(shí)才拆鉤子鉤子狀態(tài)放在被 patch 的類上絕不放在插件實(shí)例或模塊局部變量包裝內(nèi)部通過_live_plugin解析當(dāng)前插件而非捕獲安裝時(shí)的那個(gè)實(shí)例逆序釋放只在包裝仍是最外層時(shí)才還原每次調(diào)用必須冪等并保證臨時(shí)改動(dòng)的字段即便出錯(cuò)也要還原。文件里有一句很重的警告A patch that skips these rules stacks one wrapper per instance, and its effect grows with every book opened.—— 違反規(guī)則的 patch 會(huì)每開一本書多疊一層效果隨閱讀越滾越大。關(guān)鍵技巧_live_plugin告別陳舊實(shí)例_acquireHooks保證只裝一次但會(huì)帶來一個(gè)新問題那唯一一次包裝捕獲的plugin變量可能早已指向一個(gè)被銷毀的舊實(shí)例。SimpleUI 用一個(gè)模塊級指針_live_plugin解決infra/sui_patches.lua每次patchFileManagerClass被調(diào)用時(shí)刷新_live_plugin plugin包裝函數(shù)內(nèi)部一律用_live_plugin而非閉包里那個(gè)過期的plugin。這就像給每個(gè)鉤子留了一個(gè)最新聯(lián)系人永遠(yuǎn)指向當(dāng)前存活的那個(gè)插件對象從根上避免了操作了一個(gè)已斷連的舊實(shí)例這類隱蔽 bug比如導(dǎo)航欄高亮停在舊標(biāo)簽頁。實(shí)戰(zhàn)①包裝FileManager.setupLayout最典型的例子是給文件管理器加上導(dǎo)航欄。M.patchFileManagerClassinfra/sui_patches.lua先取狀態(tài)、判斷本會(huì)話是否已 patch 過local layout_state _acquireHooks(FileManager, FM_LAYOUT_STATE, plugin) local setup_already_patched (layout_state nil) local orig_setupLayout layout_state and FileManager.setupLayout若已 patch 過setup_already_patched為真就直接跳過重新包裝僅刷新_live_plugin。這樣即使文件管理器在一次會(huì)話中被重建 N 次setupLayout上也始終只有一層SimpleUI 包裝不會(huì)疊出導(dǎo)航欄套導(dǎo)航欄。?? 注釋里專門記錄了一個(gè)真實(shí)事故缺少這個(gè)守衛(wèi)時(shí)每次 FM 重建都會(huì)在外層再包一層導(dǎo)致壁紙背景被新的白色容器蓋住而消失。守衛(wèi)把這種隨生命周期膨脹的隱患徹底掐滅。實(shí)戰(zhàn)②UIManager.close的會(huì)話級守衛(wèi)UIManager是全局單例它的close被調(diào)用頻率極高。M.patchUIManagerCloseinfra/sui_patches.lua采用會(huì)話級標(biāo)志位防止疊加if UIManager._simpleui_close_patched then UIManager._simpleui_close_plugin plugin -- 僅刷新指針 plugin._orig_uimanager_close UIManager._simpleui_close_orig return end UIManager._simpleui_close_patched true local orig_close UIManager.close UIManager._simpleui_close_orig orig_close plugin._orig_uimanager_close orig_close UIManager.close function(um_self, widget, ...) ... end再次進(jìn)入時(shí)不再新建包裝只把共享插槽里的插件指針更新為最新實(shí)例。單個(gè)活著的包裝永遠(yuǎn)通過.ui拿到當(dāng)前文件管理器來做判斷從而在關(guān)閉全屏組件 → 自動(dòng)彈回主頁屏這條高頻路徑上保持輕量且正確。 類似手法還用在patchUIManagerShowinfra/sui_patches.lua、壁紙注入patchWallpaperFMinfra/sui_patches.lua等處都是標(biāo)志位守衛(wèi) 原函數(shù)存到類上的組合拳。可靠還原teardownAll的逆序大卸載安裝入口M.installAll一口氣裝上二十多個(gè) patchinfra/sui_patches.lua與之對稱的M.teardownAllinfra/sui_patches.lua則負(fù)責(zé)逐一拆干凈。它的順序很有講究壁紙鉤子最后安裝所以最先釋放因?yàn)樗B在最上層接著恢復(fù)UIManager.show/UIManager.close調(diào)用頻率最高、層級最深然后依次還原BookList.new、Menu、FileManager、readcollection等類方法最后清理模塊級狀態(tài)_hs_boot_done、D-pad 焦點(diǎn)等讓禁用→再啟用能從干凈狀態(tài)重啟。每個(gè)還原點(diǎn)都先查標(biāo)志位再動(dòng)手例如if FM and FM._simpleui_deleteFile_patched and plugin._orig_fm_deleteFile then FM.deleteFile plugin._orig_fm_deleteFile FM._simpleui_deleteFile_patched nil plugin._orig_fm_deleteFile nil end這套標(biāo)志位 原函數(shù) 逆序三件套確保插件無論被啟用/禁用多少輪最終 KOReader 都能回到與從未安裝 SimpleUI 完全一致的初始狀態(tài)。小結(jié)SimpleUI 的 monkey-patch 之所以安全且可靠靠的不是運(yùn)氣而是把三件事制度化統(tǒng)一登記——_acquireHooks/_addHook/_releaseHooks讓每個(gè)包裝都有據(jù)可查、可逆可拆冪等守衛(wèi)——標(biāo)志位與_acquireHooks的nil返回杜絕每開一本書疊一層的膨脹陷阱逆序還原 _live_plugin——teardownAll按安裝的反序干凈卸載指針始終指向存活實(shí)例。對任何想給 KOReader 寫插件、或研究 Lua 動(dòng)態(tài)修改類方法的同學(xué)infra/sui_patches.lua 都堪稱一份生產(chǎn)級 monkey-patch 范本。配合 main.lua 里的Patches.installAll/Patches.teardownAll調(diào)用時(shí)機(jī)以及 modules/module_tbr.lua、screens/sui_bottombar.lua 等被鉤子調(diào)用的具體模塊可以完整讀懂改進(jìn)去與收回來的全過程。贊分享【免費(fèi)下載鏈接】simpleui.kopluginA highly customizable UI plugin for KOReader that features a home screen, bottom navigation bar, top bar and desktop modules/widgets.項(xiàng)目地址https://gitcode.com/gh_mirrors/si/simpleui.koplugin點(diǎn)擊查看免費(fèi)下載相關(guān)推薦ContextMenuManager與系統(tǒng)還原點(diǎn)安全修改的保障ContextMenuManager與系統(tǒng)還原點(diǎn)安全修改的保障 你是否曾因誤刪注冊表項(xiàng)導(dǎo)致右鍵菜單功能異常是否擔(dān)心優(yōu)化右鍵菜單后系統(tǒng)出現(xiàn)不可預(yù)知的錯(cuò)誤本文桌面應(yīng)用系統(tǒng)工具revanced-patches安全性分析確保修改應(yīng)用的安全可靠revanced patches安全性分析確保修改應(yīng)用的安全可靠 你是否擔(dān)心使用修改版應(yīng)用會(huì)帶來安全風(fēng)險(xiǎn)ReVanced Patches作為一個(gè)開源項(xiàng)目通移動(dòng)開發(fā)快速上手Guake下拉終端一條命令裝好5個(gè)技巧讓你告別窗口來回切換快速上手Guake下拉終端一條命令裝好5個(gè)技巧讓你告別窗口來回切換 在 GNOME 桌面上寫代碼的你是不是也經(jīng)歷過這種崩潰瞬間改一行日志手忙腳亂地在編桌面應(yīng)用創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考