2026-10-13 更新:2014 年版本直接向外部存储根目录写文件,这种用法已过时。Android 10(API 29)引入分区存储,Android 11(API 30)对 targetSdk 30 及以上应用忽略
requestLegacyExternalStorage。下面改用应用内部目录,示例最低版本为 Android 4.4(API 19),不需要存储权限。Android 11 存储变化
Log2File 工具类用于 Android 程序把应用主动记录的日志写入文件,适合下面这些场景:
- 无法连接电脑进行调试,例如 USB 口被 OTG 设备占用。
- 日志不容易实时抓取。
- Bug 出现很随机,需要保留操作过程中的记录。
- 其他自己脑补。
它只保存传给 w() 的内容,不会自动读取系统 logcat;系统日志抓取仍使用 Logcat 工具。
0.说明
这篇文章最早发布在我的 CSDN 博客,后来迁移到这里。原 GitHub 源码地址在本次更新时返回 404,因此下面直接保留可阅读的替代实现。
1.Log2File 工具类源码
原实现使用静态 writer 和独立的 logInit 标志,并发写入、关闭时容易发生状态竞争。下面把一次记录过程交给一个对象管理,用同一把锁保护写入和关闭;初始化、写入或关闭失败会抛出 IOException,由调用方处理。
日志放在 context.getFilesDir()/log2file/。这个目录属于应用内部存储,不需要申请权限,卸载应用时会删除;如需分享日志,应通过 FileProvider 等方式临时授权读取。应用专属文件
代码使用 UTF-8 和 Unix epoch 毫秒时间戳,避免原 Date.toLocaleString() 带来的过时 API 与本地化格式差异。StandardCharsets 从 API 19 可用:
1 | import android.content.Context; |
每个文件在一个进程内应由同一个 Log2File 对象管理;synchronized 不会协调两个不同对象或不同进程。调用 close() 后继续写入会抛出异常;不要让记录任务和资源所有者各自随意关闭同一个对象。
这里每条日志都 flush(),适合低频、短时间诊断,但会增加 I/O。flush() 只把 Java 缓冲交给下层流,不保证断电后仍能恢复。长时间或高频采集需要明确文件大小、保留期限和写入队列上限,按场景采用批量写入及日志轮转;这个最小示例没有实现这些策略。BufferedWriter
2.Log2File 类的使用
新版使用构造函数打开文件,替代旧的静态 init()。在后台任务中创建实例,写入需要保留的事件,任务结束时关闭:
1 | // Run this on a background worker, not the UI thread. |
try-with-resources 会在正常结束和异常退出时调用 close()。需要跨多个回调记录时,由同一个任务或组件持有实例,等所有写入完成后统一关闭,不要为每条日志重新打开文件。
日志只记录排查所需的信息,避免把密码、令牌或完整用户输入写进去。应用内部目录解决的是文件归属和访问权限,不能替代内容脱敏和保留期限管理。
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
