From 8e8359aab4e0c40317a2ff79fa0c541bc243a42c Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Thu, 13 Nov 2025 04:28:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BA=90=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- appbase/build.properties | 4 +- libappbase/build.properties | 4 +- .../studio/libappbase/LogViewThread.java | 139 +++++++++++++----- 3 files changed, 104 insertions(+), 43 deletions(-) diff --git a/appbase/build.properties b/appbase/build.properties index c359ff42..3c4621e5 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Nov 12 20:21:00 GMT 2025 +#Wed Nov 12 20:28:00 GMT 2025 stageCount=10 libraryProject=libappbase baseVersion=15.10 publishVersion=15.10.9 -buildCount=21 +buildCount=22 baseBetaVersion=15.10.10 diff --git a/libappbase/build.properties b/libappbase/build.properties index c359ff42..3c4621e5 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Nov 12 20:21:00 GMT 2025 +#Wed Nov 12 20:28:00 GMT 2025 stageCount=10 libraryProject=libappbase baseVersion=15.10 publishVersion=15.10.9 -buildCount=21 +buildCount=22 baseBetaVersion=15.10.10 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java index bccbe035..56ad4c98 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java @@ -4,77 +4,138 @@ package cc.winboll.studio.libappbase; * @Author ZhanGSKen * @Date 2024/08/12 14:43:50 * @Describe 日志视图线程类 + * 独立线程监听日志文件目录变化(如写入、删除),触发日志视图更新,避免阻塞主线程 */ import android.os.FileObserver; -import cc.winboll.studio.libappbase.LogUtils; import java.lang.ref.WeakReference; public class LogViewThread extends Thread { + /** 日志标签(用于调试输出) */ public static final String TAG = "LogViewThread"; - // 线程退出标志 - volatile boolean isExist = false; - // 应用日志文件监听实例 - LogListener mLogListener; - // 日志视图弱引用 - WeakReference mwrLogView; + /** 线程退出标志(volatile 保证多线程可见性,控制循环退出) */ + private volatile boolean isExit = false; + /** 日志文件目录监听实例(监听文件写入、删除事件) */ + private LogListener mLogListener; + /** 日志视图弱引用(避免持有 LogView 强引用导致内存泄漏) */ + private final WeakReference mLogViewWeakRef; - // - // 构造函数 - // @logView : 日志显示输出视图类 + /** + * 构造函数 + * @param logView 日志显示视图实例(需通过弱引用持有,避免内存泄漏) + */ public LogViewThread(LogView logView) { - mwrLogView = new WeakReference(logView); - + // 使用弱引用包装 LogView,当视图销毁时可被 GC 回收 + mLogViewWeakRef = new WeakReference<>(logView); } - public void setIsExist(boolean isExist) { - this.isExist = isExist; + /** + * 设置线程退出标志(触发线程停止监听并退出) + * @param exit true:退出线程;false:继续运行(默认) + */ + public void setExit(boolean exit) { + this.isExit = exit; } - public boolean isExist() { - return isExist; + /** + * 获取当前线程退出状态 + * @return true:已标记退出;false:运行中 + */ + public boolean isExit() { + return isExit; } + /** + * 线程核心逻辑:初始化文件监听并启动循环,直到收到退出标志 + */ @Override public void run() { - String szLogDir = LogUtils.getLogCacheDir().getPath(); - mLogListener = new LogListener(szLogDir); + // 获取日志缓存目录路径(从 LogUtils 统一获取,确保路径一致性) + String logDirPath = LogUtils.getLogCacheDir().getPath(); + LogUtils.d(TAG, "启动日志文件监听,监听目录:" + logDirPath); + + // 初始化日志文件监听器(监听目标目录的文件事件) + mLogListener = new LogListener(logDirPath); + // 开始监听文件事件(非阻塞,内部通过 Native 层实现) mLogListener.startWatching(); - while (isExist() == false) { + + // 循环等待退出标志(每 1 秒检查一次,降低 CPU 占用) + while (!isExit()) { try { - Thread.sleep(1000); - } catch (InterruptedException e) {} + Thread.sleep(1000); // 休眠 1 秒,避免忙等 + } catch (InterruptedException e) { + // 线程被中断时,恢复中断标志并退出循环(避免无限阻塞) + Thread.currentThread().interrupt(); + LogUtils.d(TAG, "日志监听线程被中断,准备退出。" + e); + break; + } } + + // 收到退出标志,停止监听并释放资源 mLogListener.stopWatching(); + LogUtils.d(TAG, "日志文件监听已停止,线程退出"); } + /** + * 日志文件监听内部类(继承 FileObserver,监听目录下文件变化) + * 仅关注文件写入完成(CLOSE_WRITE)和文件删除(DELETE)事件 + */ + private class LogListener extends FileObserver { - // - // 日志文件监听类 - // - class LogListener extends FileObserver { + /** + * 构造函数 + * @param path 监听的目录路径(此处为日志缓存目录) + */ public LogListener(String path) { + // 父类构造:监听指定目录的所有事件(通过位掩码 ALL_EVENTS 指定) super(path); } + /** + * 文件事件回调(运行在系统私有线程,非主线程) + * @param event 事件类型(通过位掩码表示,需与 ALL_EVENTS 按位与解析) + * @param path 发生事件的文件名(相对监听目录的路径) + */ @Override public void onEvent(int event, String path) { - int e = event & FileObserver.ALL_EVENTS; - switch (e) { - case FileObserver.CLOSE_WRITE:{ - if (mwrLogView.get() != null) { - mwrLogView.get().updateLogView(); - } - break; - } - case FileObserver.DELETE:{ - if (mwrLogView.get() != null) { - mwrLogView.get().updateLogView(); - } - break; - } + // 解析事件类型(排除无关事件,只处理目标事件) + int eventType = event & FileObserver.ALL_EVENTS; + + switch (eventType) { + // 事件:文件写入完成(如日志写入结束并关闭文件) + case FileObserver.CLOSE_WRITE: + LogUtils.d(TAG, "日志文件写入完成,文件名:" + (path != null ? path : "未知")); + // 触发日志视图更新(需先判断 LogView 是否未被回收) + updateLogView(); + break; + + // 事件:文件被删除(如日志清理操作) + case FileObserver.DELETE: + LogUtils.d(TAG, "日志文件被删除,文件名:" + (path != null ? path : "未知")); + // 触发日志视图更新(刷新视图显示空状态) + updateLogView(); + break; + + default: + // 忽略其他无关事件(如文件创建、访问等) + break; + } + } + + /** + * 触发日志视图更新(通过弱引用获取 LogView,避免内存泄漏) + */ + private void updateLogView() { + // 从弱引用中获取 LogView 实例(若视图已销毁,get() 返回 null) + LogView logView = mLogViewWeakRef.get(); + if (logView != null) { + // 调用 LogView 的更新方法(需确保 updateLogView 内部处理主线程切换) + logView.updateLogView(); + } else { + LogUtils.w(TAG, "LogView 已被回收,无法更新日志视图"); } } } } +