项目要求扫描出SD卡中所有的视频文件,之前是开一个线程全盘扫描,但扫描时间太长。调用系统 MediaScannerService 进行扫描也是非常慢。万恶的产品经理对比了暴风魔镜,暴风魔镜拷入SD卡中一个视频刷新一下 1 秒钟就扫描了出来。
人家都能做到,咱们怎么做不到
哎我等屌丝只能再研究了发现了 fileobserver
android fileobserver 可以对文件系统的文件夹进行监听,但不能监听子目录。
要监听子目录必须递归遍历所有的文件夹对其添加监听。
fileobserver 在 Android 4.0 和 5.0 中是好用的,但在 Android 6.0 中有 bug 监听不能工作。
如果文件太多,递归监听非常耗内存,导致频繁 GC。
参考网上代码稍微改了一点,仅供参考
/**
* @author ddai FileWatcher support subdirectory(recursively)
*/
public class FileWatcher extends FileObserver {
static final String TAG = "FileWatcher";
ArrayList<FileObserver> mObservers;
String mPath;
int mMask;
String mThreadName = FileWatcher.class.getSimpleName();
HandlerThread mThread;
Handler mThreadHandler;
private Context mContext;
public FileWatcher(Context context, String path) {
this(path, ALL_EVENTS);
mContext = context;
}
public FileWatcher(String path) {
this(path, ALL_EVENTS);
}
public FileWatcher(String path, int mask) {
super(path, mask);
mPath = path;
mMask = mask;
}
@Override
public void startWatching() {
mThreadName = FileWatcher.class.getSimpleName();
if (mThread == null || !mThread.isAlive()) {
mThread = new HandlerThread(mThreadName, Process.THREAD_PRIORITY_BACKGROUND);
mThread.start();
mThreadHandler = new Handler(mThread.getLooper());
mThreadHandler.post(new startRunnable());
}
}
@Override
public void stopWatching() {
if (null != mThreadHandler && null != mThread && mThread.isAlive()) {
mThreadHandler.post(new stopRunnable());
}
mThreadHandler = null;
mThread.quit();
mThread = null;
}
@Override
public void onEvent(int event, String path) {
event = event & FileObserver.ALL_EVENTS;
final String tmpPath = path;
switch (event) {
case FileObserver.ACCESS:
// Log.i("FileWatcher", "ACCESS: " + path);
break;
case FileObserver.ATTRIB:
// Log.i("FileWatcher", "ATTRIB: " + path);
break;
case FileObserver.CLOSE_NOWRITE:
// Log.i("FileWatcher", "CLOSE_NOWRITE: " + path);
break;
case FileObserver.CLOSE_WRITE:
Log.i("FileWatcher", "CLOSE_WRITE: " + path);
// 文件写入完毕后会回调,可以在这对新写入的文件做操作
mThreadHandler.post(new Runnable() {
@Override
public void run() {
//
}
});
break;
case FileObserver.CREATE:
Log.i(TAG, "CREATE: " + path);
mThreadHandler.post(new Runnable() {
@Override
public void run() {
doCreate(tmpPath);
}
});
break;
case FileObserver.DELETE:
Log.i(TAG, "DELETE: " + path);
mThreadHandler.post(new Runnable() {
@Override
public void run() {
doDelete(tmpPath);
}
});
break;
case FileObserver.DELETE_SELF:
// Log.i("FileWatcher", "DELETE_SELF: " + path);
break;
case FileObserver.MODIFY:
// Log.i("FileWatcher", "MODIFY: " + path);
break;
case FileObserver.MOVE_SELF:
// Log.i("FileWatcher", "MOVE_SELF: " + path);
break;
case FileObserver.MOVED_FROM:
// Log.i("FileWatcher", "MOVED_FROM: " + path);
break;
case FileObserver.MOVED_TO:
// Log.i("FileWatcher", "MOVED_TO: " + path);
break;
case FileObserver.OPEN:
Log.i("FileWatcher", "OPEN: " + path);
break;
default:
Log.i(TAG, "DEFAULT(" + event + ";) : " + path);
break;
}
}
private void doCreate(String path) {
synchronized (FileWatcher.this) {
File file = new File(path);
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
// 新建文件夹,对该文件夹及子目录添加监听
Stack<String> stack = new Stack<String>();
stack.push(path);
while (!stack.isEmpty()) {
String parent = stack.pop();
SingleFileObserver observer = new SingleFileObserver(parent, mMask);
observer.startWatching();
mObservers.add(observer);
Log.d(TAG, "add observer " + parent);
File parentFile = new File(parent);
File[] files = parentFile.listFiles();
if (null == files) {
continue;
}
for (File f : files) {
if (f.isDirectory() && !f.getName().equals(".") && !f.getName().equals("..")) {
stack.push(f.getPath());
}
}
}
stack.clear();
stack = null;
} else {
// 新建文件
}
}
}
private void doDelete(String path) {
synchronized (FileWatcher.this) {
Iterator<FileObserver> it = mObservers.iterator();
while (it.hasNext()) {
SingleFileObserver sfo = (SingleFileObserver) it.next();
// 如果删除的是文件夹移除对该文件夹及子目录的监听
if (sfo.mPath != null && (sfo.mPath.equals(path) || sfo.mPath.startsWith(path + "/"))) {
Log.d(TAG, "stop observer " + sfo.mPath);
sfo.stopWatching();
it.remove();
sfo = null;
}
}
}
}
/**
* 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) {
if (path == null) {
return;
}
String newPath = mPath + "/" + path;
FileWatcher.this.onEvent(event, newPath);
}
}
class startRunnable implements Runnable {
@Override
public void run() {
synchronized (FileWatcher.this) {
if (mObservers != null) {
return;
}
mObservers = new ArrayList<FileObserver>();
Stack<String> stack = new Stack<String>();
stack.push(mPath);
while (!stack.isEmpty()) {
String parent = String.valueOf(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());
}
}
}
Iterator<FileObserver> it = mObservers.iterator();
while (it.hasNext()) {
SingleFileObserver sfo = (SingleFileObserver) it.next();
sfo.startWatching();
}
}
}
}
class stopRunnable implements Runnable {
@Override
public void run() {
synchronized (FileWatcher.this) {
if (mObservers == null) {
return;
}
Iterator<FileObserver> it = mObservers.iterator();
while (it.hasNext()) {
FileObserver sfo = it.next();
sfo.stopWatching();
}
mObservers.clear();
mObservers = null;
}
}
}
}