Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android -- FileObserver 類用法及限制

android -- FileObserver 類用法及限制

編輯:關於Android編程

最近有個需求需要監控目錄下的文件及目錄情況,查了一下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、創建目錄監聽器


[java] 
<SPAN style="FONT-SIZE: 14px">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);  
                    //}  
                } 
                } 
            } 
        } 
    };</SPAN> 

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);


消息處理:

 

[java] 
<SPAN style="FONT-SIZE: 14px">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); 
                    }        
            } 
        } 
};</SPAN> 

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 類:


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


不過拿下來後編譯發現一點問題,我的代碼如下:(修改了一下編譯錯誤而已,代碼抄自上面)

 

 


[java] 
<SPAN style="FONT-SIZE: 14px">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); 
        } 
    } 

</SPAN> 

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);
  }
 }
}


 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved