项目中有个需要,需要根据接收后台推送的文件,然后拷贝到指定目录。这个功能用FileObserver 可以实现,当监测目录有新文件时,会触发FileObserver.onEvent(),在onEvent 里拷贝文件就可以了。
private void startFileObserverThread(){
Runnable structexce = new Runnable() {
public void run() {
int flags = FileObserver.CREATE|FileObserver.MOVED_TO;//1.在监测路径中新建文件、子文件夹或将文件、子文件夹移动到监测路径
mObserver = new FileObserver(SRC_PATH, flags) {
public void onEvent(int event, String path) {//2.监测事件发生,触发onEvent
targetFile = null;
File[] files = new File(SRC_PATH).listFiles();
if(files==null){
return;
}
try {
Thread.sleep(10000);//3.加个延时,等后台把文件传完
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0; i<files.length; i++){
File f = files[i];
if(f.getName().matches("\\S*\\.zip$") && f.isFile()){
Log.d(TAG, "find target file: " +f.getPath());
targetFile = f;
break;
}
}
if ( targetFile != null) {
for(int j=0; j<10; j++){//4.以防万一,最多拷贝10次
try {
copy(targetFile.getPath(), DST_FILE);
} catch (IOException e) {
Log.e(TAG, "Could not copy animation.zip");
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
md5src = getFileMD5(targetFile);
md5dst = getFileMD5(new File(DST_FILE));
if (md5src != null && md5dst != null && md5src.equals(md5dst))//5.md5校验通过,认为拷贝成功
break;
}
if (md5src != null && md5dst != null && md5src.equals(md5dst)) {
File[] namefiles = new File(NAME_PATH).listFiles();//6.将文件名同名文件记录在NAME_PATH路径下
if(namefiles!=null){
Log.d(TAG,NAME_PATH + " : is not empty");
for(int i=0; i<namefiles.length; i++){
File fi = namefiles[i];
removeFile(fi.getPath());
}
}
String name[] = targetFile.getName().split("\\.zip");
File zipname = new File(NAME_PATH +"/" + name[0]);
Log.d(TAG, "path = " + zipname.getPath());
try {
FileWriter fileWritter = new FileWriter(zipname.getPath(),true);
fileWritter.write(md5dst);//7.记录文件md5值,以供后台校验
fileWritter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
removeFile(targetFile.getPath());//8.删除原文件
}
}
};
mObserver.startWatching();//9.开如检测
}
};
Thread tFileObsv = new Thread(structexce);
tFileObsv.start();
}
上面用到的文件删除、文件拷贝、md5计算也一并贴出来吧:
private boolean removeFile(String path) {
Log.d(TAG, "starting removing " + path);
File file = new File(path);
if (null == file) {
return false;
}
if (!file.exists()) {
Log.d(TAG, "rm file not exists: " + path);
return true;
}
if (!file.delete()) {
Log.d(TAG, "rm error: " + path);
return false;
}
Log.d(TAG, " removing " + path + " succedeed");
return true;
}
private void copy(String src, String dst) throws IOException {
Log.d(TAG, " copying " + src + " to " + dst);
InputStream in = new FileInputStream(src);
OutputStream out = null;
byte[] buf = new byte[1024*1024*80];
int len;
try {
out = new FileOutputStream(dst);
try {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.flush();
} finally {
out.close();
}
} finally {
in.close();
}
}
public static String getFileMD5(File file) {
if (!file.isFile()) {
return null;
}
MessageDigest digest = null;
FileInputStream in = null;
byte buffer[] = new byte[1024];
int len;
try {
digest = MessageDigest.getInstance("MD5");
in = new FileInputStream(file);
while ((len = in.read(buffer, 0, 1024)) != -1) {
digest.update(buffer, 0, len);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
BigInteger bigInt = new BigInteger(1, digest.digest());
return bigInt.toString(16);
}
问题总结
- 因为要记录后台推送的文件名,我就创建了一个文件名同名的文件,并在该文件中写入原文件的MD5值以供校验。但是起初我把该文件创建在监测路径下,这就导致有文件传来 -> 触发 onEvent -> 记录文件名 -> onEvent,这样onEvent就嵌套了,我曾尝试在onEvent 中先stopWatching,在onEvent 最后再重新startWatching,但这样还是不行,看了一下源码中stopWatching的说明,运行中的事件可能会继续上报(Some events may be in process, so events may continue to be reported even after this method completes. )。所以改在其他路径下创建文件了。
- 考虑到文件推送过程中有断电的可能,在开机时又检查了一下监测路径并清空。
- 创建监测路径是在device/rockchip/rk3288/init.rk3288.rc中:
on post-fs-data
mkdir /data/media/XXX 0777 system sdcard_rw
由于/data/media目录是在on post-fs-data 时创建的:
on post-fs-data
# we will remap this as /mnt/sdcard with the sdcard fuse tool
mkdir /data/media 0770 media_rw media_rw
所以一开时我写成:
on post-fs
mkdir /data/media/XXX 0777 system sdcard_rw
创建路径失败。
- 读写权限设置
Android 文件系统权限定义在system/core/include/private/android_filesystem_config.h 中:
...
#define AID_MEDIA_RW 1023 /* internal media storage write access */
#define AID_MTP 1024 /* MTP USB driver access */
#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
#define AID_DRMRPC 1026 /* group for drm rpc */
#define AID_NFC 1027 /* nfc subsystem */
#define AID_SDCARD_R 1028 /* external storage read access */
...
内部存储读写权限group是1023,所以在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 中加入1023 group:
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};