mirror of
http://gitea.winboll.cc/Studio/WinBoLL.git
synced 2026-06-30 04:22:25 +08:00
150 lines
5.4 KiB
Java
150 lines
5.4 KiB
Java
package cc.winboll.studio.libappbase;
|
||
|
||
import android.os.FileObserver;
|
||
import java.io.File;
|
||
import java.lang.ref.WeakReference;
|
||
|
||
/**
|
||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||
* @Date 2024/08/12 14:43:50
|
||
* @Describe 日志视图线程类
|
||
* 独立线程监听日志文件目录变化(如写入、删除),触发日志视图更新,避免阻塞主线程
|
||
*/
|
||
public class LogViewThread extends Thread {
|
||
|
||
/** 日志标签(用于调试输出) */
|
||
public static final String TAG = "LogViewThread";
|
||
|
||
/** 线程退出标志(volatile 保证多线程可见性,控制循环退出) */
|
||
private volatile boolean isExit = false;
|
||
/** 日志文件目录监听实例(监听文件写入、删除事件) */
|
||
private LogListener mLogListener;
|
||
/** 日志视图弱引用(避免持有 LogView 强引用导致内存泄漏) */
|
||
private final WeakReference<LogView> mLogViewWeakRef;
|
||
|
||
/**
|
||
* 构造函数
|
||
* @param logView 日志显示视图实例(需通过弱引用持有,避免内存泄漏)
|
||
*/
|
||
public LogViewThread(LogView logView) {
|
||
// 使用弱引用包装 LogView,当视图销毁时可被 GC 回收
|
||
mLogViewWeakRef = new WeakReference<>(logView);
|
||
}
|
||
|
||
/**
|
||
* 设置线程退出标志(触发线程停止监听并退出)
|
||
* @param exit true:退出线程;false:继续运行(默认)
|
||
*/
|
||
public void setExit(boolean exit) {
|
||
this.isExit = exit;
|
||
}
|
||
|
||
/**
|
||
* 获取当前线程退出状态
|
||
* @return true:已标记退出;false:运行中
|
||
*/
|
||
public boolean isExit() {
|
||
return isExit;
|
||
}
|
||
|
||
/**
|
||
* 线程核心逻辑:初始化文件监听并启动循环,直到收到退出标志
|
||
*/
|
||
@Override
|
||
public void run() {
|
||
// 调试状态进行日志输出任务
|
||
if (GlobalApplication.isDebugging()) {
|
||
// 获取日志缓存目录路径(从 LogUtils 统一获取,确保路径一致性)
|
||
File logDir = LogUtils.getLogCacheDir();
|
||
if (logDir == null) {
|
||
LogUtils.d(TAG, "日志缓存目录未初始化,线程退出");
|
||
return;
|
||
}
|
||
String logDirPath = logDir.getPath();
|
||
LogUtils.d(TAG, "启动日志文件监听,监听目录:" + logDirPath);
|
||
|
||
// 初始化日志文件监听器(监听目标目录的文件事件)
|
||
mLogListener = new LogListener(logDirPath);
|
||
// 开始监听文件事件(非阻塞,内部通过 Native 层实现)
|
||
mLogListener.startWatching();
|
||
|
||
// 循环等待退出标志(每 1 秒检查一次,降低 CPU 占用)
|
||
while (!isExit()) {
|
||
try {
|
||
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 {
|
||
|
||
/**
|
||
* 构造函数
|
||
* @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 eventType = event & FileObserver.ALL_EVENTS;
|
||
|
||
switch (eventType) {
|
||
// 事件:文件写入完成(如日志写入结束并关闭文件)
|
||
case FileObserver.CLOSE_WRITE:
|
||
// 触发日志视图更新(需先判断 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 已被回收,无法更新日志视图");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|