写了一个日志库,使用了logback框架,此框架是在xml中配置日志目录的,最近发现无法完成写日志的操作,logback框架报创建目录失败,我配置的目录是sdcard中的应用目录,之前在别的项目用都好好的,有些项目又不行,于是慢慢写代码找问题,终于还是找到了,Android的应用目录不能手动创建,只能使用系统API自动创建,如下代码:
val dir = File("/storage/emulated/0/Android/data/com.evendai.logdemo/files/Documents")
val created = dir.mkdirs()
Log.i("ABCD", "目录存创建结果: $created")
打印结果为false,说明无法创建出所需要的目录,按官方文档说,这个应用目录是不需要读写文件权限的,所以当时我就很奇怪,后来不小心才发现,需要用系统Api自动创建,如下:
val dir = File("/storage/emulated/0/Android/data/com.evendai.logdemo/files/Documents")
Log.i("ABCD", "目录存在吗:${dir.exists()}")
val document = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
Log.i("ABCD", "目录存在吗:${dir.exists()}")
输出结果如下:
目录存在吗:false
目录存在吗:true
从Log可以看出,在调用了getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)方法之后,系统就会自动创建出对应的目录,系统创建出应用目录之后,我们就可以在里面自由的创建目录和文件了,示例如下:
val documentDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
val logDir = File(documentDir, "/logs")
if (!logDir.exists()) logDir.mkdirs() // 如果log目录不存在,必须先创建
val logFile = File(logDir, "log.txt")
BufferedWriter(OutputStreamWriter(FileOutputStream(logFile))).use {
it.write("Hello World!")
}
Log.i("ABCD","logFile是否存在:${logFile.exists()}")
需要注意的是,我们需要用新的目录时,需要先调用mkdirs创建出来之后才能往里面写文件,而写文件时,不需要调用file.createNewFile()来创建文件,直接使用输出流写东西就会自动创建出文件了。
OK,到这里,我们就可以知道了logback是直接使用目录路径然后调用mkdirs来创建应用目录的,所以才会创建失败,解决方案也很简单,我们没法修改logback框架,但是我们可以在使用logback前先调用getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)让系统创建出应用的目录,这样logback再创建目录时就不会失败了,示例如下:
context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
val logger = LoggerFactory.getLogger(Timber::class.java)
logger.info("Hello World!")
这里需要注意的是Environment.DIRECTORY_DOCUMENTS是API 19(Android4.4)才出来的,想兼容到更低版本,这让我不得不去了解一下getExternalFilesDir()函数的声明,通过官方声明了解到,如果参数传null,则返回应用的文件的根目录,如果传了具体的值,则返回具体值的子目录,示例如下:
getExternalFilesDir(null)
getExternalFilesDir("mydir")
返回的目录为:
/sdcard/Android/data/应用包名/files
/sdcard/Android/data/应用包名/files/mydir
而且,目录是自动创建,一调用getExternalFilesDir()方法就会自动创建,所以getExternalFilesDir方法中传什么就会生成对应的子目录,系统自带常量Environment.DIRECTORY_MOVIES、Environment.DIRECTORY_MUSIC等,其实也就是一些字符串,你不写这些常量,直接写字符串也是可以的,这些常量只不过是定义了一些通用的目录名称而已。
了解到这,我的Log库就不能使用Environment.DIRECTORY_DOCUMENTS常量了,因为它是API19才出的,限制了我的版本,我们使用getExternalFilesDir(null)即可,或者getExternalFilesDir(“Documents”)。
另外,从文档中还了解到了一些不为人知的秘密,从API19(Android4.4)版本开始,读写getExternalFilesDir()目录才不需要读写权限的,所以为了兼容低版本,我的Log库声明了读写权限。
另外,从文档中还了解到,可以调用MediaScannerConnection.scanFile来让系统发现我们的媒体文件,而且也提供了示例代码,所以,有事没事,多看看官方文档,可以学到许多的知识点。