现在的位置: 首页 > 综合 > 正文

android — FileObserver 类用法及限制

2013年08月20日 ⁄ 综合 ⁄ 共 6295字 ⁄ 字号 评论关闭

最近有个需求需要监控目录下的文件及目录情况,查了一下android上面正在有个类:FileObserver,下面简要说明一下用法及限制。

android.os.FileObserver
Monitors files (using inotify) to fire an event after files are accessed or changed by by any process on the device (including this one). FileObserver is an abstract class; subclasses must implement the event handler onEvent(int, String).
Each FileObserver instance monitors a single file or directory. If a directory is monitored, events will be triggered for all files and subdirectories inside the monitored directory.
An event mask is used to specify which changes or actions to report. Event type constants are used to describe the possible changes in the event mask as well as what actually happened in event callbacks.

FileObserver类是一个用于监听文件访问、创建、修改、删除、移动等操作的监听器,基于linux的inotify。 
FileObserver 是个抽象类,必须继承它才能使用。每个FileObserver对象监听一个单独的文件或者文件夹,如果监视的是一个文件夹,
那么文件夹下所有的文件和级联子目录的改变都会触发监听的事件。

其实不然,经过测试并不支持递归,对于监听目录的子目录中的文件改动,FileObserver 对象是无法收到事件回调的,不仅这样,
监听目录的子目录本身的变动也收不到事件回调。原因是由 linux 的 inotify 机制本身决定的,基于 inotify 实现的 FileObserver 自然也不支持递归监听。

1、概述用法

FileObserver 是个抽象类,必须继承它才能使用

可以监听的事件类型:
ACCESS : 即文件被访问
MODIFY : 文件被修改
ATTRIB : 文件属性被修改,如 chmod、chown、touch 等
CLOSE_WRITE : 可写文件被 close
CLOSE_NOWRITE : 不可写文件被 close
OPEN : 文件被 open
MOVED_FROM : 文件被移走,如 mv
MOVED_TO : 文件被移来,如 mv、cp
CREATE : 创建新文件
DELETE : 文件被删除,如 rm
DELETE_SELF : 自删除,即一个可执行文件在执行时删除自己
MOVE_SELF : 自移动,即一个可执行文件在执行时移动自己
CLOSE : 文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
ALL_EVENTS : 包括上面的所有事件
可以SDK DEVELOP上有更详细的介绍

1、创建目录监听器

class MyFileObserver extends FileObserver {
		// 这种构造方法是默认监听所有事件的,如果使用super(String,int)这种构造方法,则int参数是要监听的事件类型。
		// 为了防止嵌套消息调用,建议使用 super(String,int) 按需创建监控消息值
		public MyFileObserver(String path) {
			super(path, FileObserver.CLOSE_WRITE | FileObserver.CREATE | FileObserver.DELETE
					| FileObserver.DELETE_SELF );
			//super(path,FileObserver.ALL_EVENTS);
		}

		@Override
		public void onEvent(int event, String path) {
			synchronized (mutex) {
				// 这里注意 event 值是与 0x40000000或上后的值,所以需要case时需要先进行 &FileObserver.ALL_EVENTS
				int el = event & FileObserver.ALL_EVENTS;
				Log.i(TAG, "onEvent event:" + el + " path:" + path);
				switch(el){
					//TODO 这里case所有的监听的事件类型 FileObserver.xx 静态属性
					//如果要在onEvent中做较多操作,最好使用线程去做 ,以免因为阻塞接收不到后面的事件。
					
					//try {
					//	handler.obtainMessage(el, path).sendToTarget();
					//} catch (Exception e) {
					//	Log.e(TAG, "Monitor FileObserver.onEvent error:" + e);
					//}
				}
				}
			}
		}
	};


使用线程处理:
private Handler handler = null;
private HandlerThread th = null;

th = new HandlerThread("sectionStorage");
th.start();
handler = new Handler(th.getLooper(), callback);

fobs = new MyFileObserver(root);

消息处理:

Handler.Callback callback = new Handler.Callback() {
		@Override
		public boolean handleMessage(Message msg) {
			Log.i(TAG, "callback msg.what=" + msg.what + " msg.obj:" + msg.obj);
			synchronized (mutex) {
				try {
						switch (msg.what) {
						case FileObserver.CLOSE_WRITE:
							onFileWriteClosed((String) msg.obj);
							break;
						case FileObserver.CREATE:
							onFileCreate((String) msg.obj);
							break;
						case FileObserver.DELETE:
							onFileDeleted((String) msg.obj);
							break;
						case FileObserver.DELETE_SELF:
							onRootDeleted();
							break;
						}
					} catch (Throwable e) {
						Log.e(TAG, "handleMessage error:" + e);
					}		
			}
		}
};


2、给目录设置监听器

MyFileObserver listener = new MyFileObserver("目录");
//开始监听     
listener.startWatching(); 
    
/*   
* 在这里做一些操作,比如创建目录什么的   
*/    
 
//停止监听  
listener.stopWatching(); 


2、对于递归监听的实现代码,可参考这里:

https://code.google.com/p/collimator/source/browse/trunk/src/com/toraleap/collimator/util/RecursiveFileObserver.java?spec=svn22&r=22

摘抄如下:
为了监听整个文件系统的变化,必须得实现这个递归监听,怎么办呢?先查查大家是怎么用 inotify 实现递归监听的,
搜索结果说明了一切,对每个子目录递归的调用 inotify,也就是说在 Android 中,也得通过遍历目录树,建立一系列 
FileObserver 对象来实现这个功能
,很笨吧。本来很担心这样做对系统的效率影响极大,但是大家都是这么实现的,
我也本着实践出真知的原则,写下了如下的 RecursiveFileObserver 类:

====================================================================================================================

不过拿下来后编译发现一点问题,我的代码如下:(修改了一下编译错误而已,代码抄自上面)


package com.test.fileobserver;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import android.os.FileObserver;
import android.util.Log;

@SuppressWarnings(value = { "rawtypes", "unchecked" })
public class RecursiveFileObserver extends FileObserver {

	/** Only modification events */
	public static int CHANGES_ONLY = CREATE | DELETE | CLOSE_WRITE
			| DELETE_SELF | MOVE_SELF | MOVED_FROM | MOVED_TO;

	List mObservers;
	String mPath;
	int mMask;

	public RecursiveFileObserver(String path) {
		this(path, ALL_EVENTS);
	}

	public RecursiveFileObserver(String path, int mask) {
		super(path, mask);
		mPath = path;
		mMask = mask;
	}

	@Override
	public void startWatching() {
		if (mObservers != null)
			return;

		mObservers = new ArrayList();
		Stack stack = new Stack();
		stack.push(mPath);

		while (!stack.isEmpty()) {
			String parent = (String)stack.pop();
			mObservers.add(new SingleFileObserver(parent, mMask));
			File path = new File(parent);
			File[] files = path.listFiles();
			if (null == files)
				continue;
			for (File f : files) {
				if (f.isDirectory() && !f.getName().equals(".")
						&& !f.getName().equals("..")) {
					stack.push(f.getPath());
				}
			}
		}

		for (int i = 0; i < mObservers.size(); i++) {
			SingleFileObserver sfo = (SingleFileObserver) mObservers.get(i);
			sfo.startWatching();
		}
	};

	@Override
	public void stopWatching() {
		if (mObservers == null)
			return;

		for (int i = 0; i < mObservers.size(); i++) {
			SingleFileObserver sfo = (SingleFileObserver) mObservers.get(i);
			sfo.stopWatching();
		}
		
		mObservers.clear();
		mObservers = null;
	};

	@Override
	public void onEvent(int event, String path) {
		switch (event) {
		case FileObserver.ACCESS:
			Log.i("RecursiveFileObserver", "ACCESS: " + path);
			break;
		case FileObserver.ATTRIB:
			Log.i("RecursiveFileObserver", "ATTRIB: " + path);
			break;
		case FileObserver.CLOSE_NOWRITE:
			Log.i("RecursiveFileObserver", "CLOSE_NOWRITE: " + path);
			break;
		case FileObserver.CLOSE_WRITE:
			Log.i("RecursiveFileObserver", "CLOSE_WRITE: " + path);
			break;
		case FileObserver.CREATE:
			Log.i("RecursiveFileObserver", "CREATE: " + path);
			break;
		case FileObserver.DELETE:
			Log.i("RecursiveFileObserver", "DELETE: " + path);
			break;
		case FileObserver.DELETE_SELF:
			Log.i("RecursiveFileObserver", "DELETE_SELF: " + path);
			break;
		case FileObserver.MODIFY:
			Log.i("RecursiveFileObserver", "MODIFY: " + path);
			break;
		case FileObserver.MOVE_SELF:
			Log.i("RecursiveFileObserver", "MOVE_SELF: " + path);
			break;
		case FileObserver.MOVED_FROM:
			Log.i("RecursiveFileObserver", "MOVED_FROM: " + path);
			break;
		case FileObserver.MOVED_TO:
			Log.i("RecursiveFileObserver", "MOVED_TO: " + path);
			break;
		case FileObserver.OPEN:
			Log.i("RecursiveFileObserver", "OPEN: " + path);
			break;
		default:
			Log.i("RecursiveFileObserver", "DEFAULT(" + event + " : " + path);
			break;
		}
	}

	/**
	 * Monitor single directory and dispatch all events to its parent, with full
	 * path.
	 */
	class SingleFileObserver extends FileObserver {
		String mPath;

		public SingleFileObserver(String path) {
			this(path, ALL_EVENTS);
			mPath = path;
		}

		public SingleFileObserver(String path, int mask) {
			super(path, mask);
			mPath = path;
		}

		@Override
		public void onEvent(int event, String path) {
			String newPath = mPath + "/" + path;
			RecursiveFileObserver.this.onEvent(event, newPath);
		}
	}
}


抱歉!评论已关闭.