Talking about Fileobserver from the inotify mechanism

Source: Internet
Author: User
Tags inotify

In some cases, we inevitably need to monitor some of the changes in the file, how to do it? Naturally we would like to use a thread, each time to look at the file situation, which is essentially based on the rotation of time scheduling. Although we can achieve our needs, However, this approach is only suitable for situations where the file is constantly changing, is very inefficient in other cases, and may throw away certain types of changes, that is, this approach does not enable real-time file monitoring.

INotify Introduction

Is there any other way? Children who are familiar with Linux should remember to introduce the inotify mechanism from Linux 2.6.13, which is used in the kernel to notify file changes: Any change in a file will produce an event to tell the user.

Event types can be monitored

First, let's take a look at several file change events that inotify can monitor:

Event Type Description
In_access File is accessed
In_modify File is modified
In_attrib File properties are modified
In_close_write Writable files are closed
In_close_nowrite Non-writable files are closed
In_close The file is closed, as well as the set of both
In_open File is opened
In_moved_from File is moved to
In_moved_to File is removed
In_move The file is moved, which is the set of the two above
In_create File is created
In_delete File is deleted
In_delete_self Self-deletion, which is an executable file that tries to delete itself when executing
In_move_self Self-moving, which is an executable file that tries to move itself when executing
In_unmount The host file system is uninstalled
API description

Next, let's make a brief description of the Innotify API:

Method Description
Inotify_init Used to create a inotify instance that returns a file descriptor that points to the instance
Inotify_add_watch Adding monitoring of files or directories, you can specify that you need to monitor those file change events
Inotify_rm_watch Remove a monitoring file or directory from the monitoring list
Read Read event information
Close Closes the file descriptor and removes all monitoring on the descriptor
Event notification

In INotify, the file event is represented by the struct inotify_event, and its structure is as follows:

struct inotify_event{  int wd;               //监控目标的watch描述符  uint32_t mask;        //事件掩码  uint32_t cookie;      //事件同步cookie  uint32_t len;         //name字符串的长度  char name __flexarr;  //被监视目标的路径名  };

One thing to keep in mind here is that the name field is not always available, only the target being monitored is a directory, and the resulting event is related to a file or subdirectory inside the directory, and the Name field is provided for the response if it is not related to the directory itself.

Use process

The following steps are typically required to implement monitoring of a file or directory:

1. Create a INotify instance

In the application, you first need to create the INotify instance:

int fd=inotify_init(); 
2. Add Monitoring
int wd=inotify_add_watch(fd,path,mask)

The FD here is the file descriptor returned by the Inotify_init () method. Each file descriptor has a sorted sequence of events.
Path is the file or directory that needs to be monitored.
Mask is the event mask, which indicates which events the application is interested in.
File system-generated events are described by watch, which returns a handle to the Watch object, WD.

3. Wait for event and loop processing

In a loop, when an event occurs, multiple events can be obtained at one time through the read () method, with the following simple code:

//事件数组,一次最多接受128个事件char event_buf[128];while(true){     int num_bytes=read(fd,event_buf,len);     //....省略....    }

Event_buf is an array of events that accepts events (Inotify_event) that are generated by file changes. len Specifies the length to read. Generally, Len is larger than the size of the event array, and many times we take the size of the event array directly as Len.
It is also important to note that the read () function is blocking, and if no event is generated, the method will remain blocked.

4. Stop Monitoring

When you need to stop monitoring, you need to delete watch for the file descriptor:

int r=inotify_rm_watch(fd,wd);

The FD here is also the file descriptor that is returned when the inotify is created, and WD is the handle to the Watch object mentioned above.

Now we simply introduced inotity related knowledge, and did not do too much drill down, interested in children's shoes can self-study the relevant Linux in this area of the source. Our focus is on the implementation of Fileobserver in Android. Next, We are really beginning to understand how the Fileobserver is implemented:

Fileobserver Implementation principle

We know that the Linux kernel for Android 1.5 is already 2.6.26, so you can use the inotify mechanism on Android to monitor your files. Google is clearly aware of this and helps us encapsulate it on a inotify basis- INotify mechanism encapsulated as Fileobserver abstract class to enable monitoring of file access, creation, modification, deletion and other operations

Next, take a look at how Fileobserver uses inotify to implement file monitoring.

Monitoring thread Initialization

There is a static inner class observerthread in Fileobserver, which is the process of implementing file monitoring:

 Public Abstract  class fileobserver {    //events that can be monitored     Public Static Final intall_events = ACCESS | MODIFY | ATTRIB | Close_write | Close_nowrite | OPEN | Moved_from | moved_to | DELETE | CREATE | delete_self | move_self;Private Static  class observerthread extends Thread {        //.... Omit multiple lines of code ....}Private StaticObserverthread S_observerthread;Static{S_observerthread =NewObserverthread ();    S_observerthread.start (); }//.... Omit multiple lines of code ....}

It's not hard to find out that Fileobserver constructs the S_observerthread object in a static block of code, let's look at its construction process:

     publicObserverThread() {            super("FileObserver");            m_fd = init();        }

We found here, call the Natvie method init (), and in that case, let's go deep and see the implementation of the Init () method (now, is it the benefit of discovering that we compiled the source code ourselves?) The implementation of this method is/frameworks/base/core/jni/android_util_fileobserver.cpp

static jint android_os_fileobserver_init(JNIEnv* env, jobject object){#if defined(__linux__)    return (jint)inotify_init();#else    return -1;#endif}

The implementation is very simple, which is to call Inotify_init () in inotify to create a inotify instance. Go back to Fileobserver to see the start of S_observerthread:

publicvoidrun() {            observe(m_fd);        }

This is also called the Natvie method observe(int fd) , to see its implementation:

Static voidAndroid_os_fileobserver_observe (jnienv* env, JobjectObject, Jint FD) {#if defined (__linux__)    //Set event array    Charevent_buf[ +];structinotify_event*Event;//cyclic processing of Read Events     while(1)    {intEvent_pos =0;//Read Events        intNum_bytes = Read (FD, EVENT_BUF,sizeof(EVENT_BUF));if(Num_bytes < (int)sizeof(*Event))        {if(errno = = eintr)Continue; Aloge ("* * * * error! Android_os_fileobserver_observe () got a short event! ");return; } while(Num_bytes >= (int)sizeof(*Event))        {intEvent_size;Event= (structInotify_event *) (event_buf + event_pos); jstring path = NULL;if(Event->len >0) {Path = Env->newstringutf (Event->name); }//Call the OnEvent method in ObserverthreadEnv->callvoidmethod (Object, Method_onevent,Event-&GT;WD,Event->mask, path);if(Env->exceptioncheck ())                {Env->exceptiondescribe ();            Env->exceptionclear (); }if(Path! = NULL)            {env->deletelocalref (path); } event_size =sizeof(*Event) +Event->len;            Num_bytes-= event_size;        Event_pos + = Event_size; }    }#endif }

It is not difficult to see that the loop here is mainly to remove the event from Inotity and then callback the method in Observerthread onEvent() . Now, go back to the method in Observerthread onEvent() :

 Public void onEvent(intWfdintMask, String Path) {//Look up we observer, fixing up the map if necessary ...Fileobserver Observer =NULL; Synchronized (m_observers) {//Find Fileobserver objects according to WFDWeakReference weak = M_observers.Get(WFD);if(Weak! =NULL) {//can happen with lots of events from a dead wfdObserver = (fileobserver) weak.Get();if(Observer = =NULL) {when//observer has been reclaimed, the object is removed from the m_observersM_observers.remove (WFD); }                }            }//Observer without the sync lock held            if(Observer! =NULL) {Try{//callback to handle the OnEvent method in FileobserverObserver.onevent (mask, path); }Catch(Throwable throwable) {LOG.WTF (Log_tag,"Unhandled exception in Fileobserver"+ Observer, Throwable); }            }        }

Fileobserver is an onEvent() abstract method that requires you to inherit fileobserver and implement the method in which to do the related operations.
So far we have seen how observerthread is started, how to get events in INotify, and callbacks to the upper layer for processing.

Start monitoring

The table above mentions the M_observers table, which maintains the registered Fileobserver object. Next, let's look at the method in Fileobserver startWatching() , which registers the Fileobserver object and is the process of initiating monitoring:

publicvoidstartWatching() {        if0) {            this);        }    }

The specific registration operation is delegated to startwatching () in S_observerthread:

public   int  startwatching  (String path, int  Mask,            Fileobserver observer) {//call native method startwatching and get a handle to a watch object             int  wfd = startwatching (m_fd, path, mask);            Integer i = new  Integer (WFD); if  (WFD >= 0 ) {synchronized  (m_observers) {//associates the Watch object handle with the current fileobserver  m_observers.put (i, new  Weakr                EFERENCE (Observer));        }} return  i; }

The method also calls the native method, which is implemented as follows:

staticobject, jint fd, jstring pathString, jint mask){    int res = -1;#if defined(__linux__)    if0)    {        constchar* path = env->GetStringUTFChars(pathString, NULL);        res = inotify_add_watch(fd, path, mask);        env->ReleaseStringUTFChars(pathString, path);    }#endif    return res;}

It is not hard to see that this inotify_add_watch() adds a watch object to the INotify object generated above by INotify and returns the handle of the Watch object to Observerthread.

Stop monitoring

So far we've learned how to register a watch handle to a Fileobserver object. With the process of registration, of course, the process of anti-registration. Similarly, Fileobserver provides us with the stopWatching() process of implementing anti-registration, which is to stop monitoring:

publicvoidstopWatching() {        if0) {//已经注册过的才能反注册            s_observerThread.stopWatching(m_descriptor);            m_descriptor = -1;        }    }

The specific implementation is also given to the S_observerthread stopWatching() method:

publicvoidstopWatching(int descriptor) {            stopWatching(m_fd, descriptor);        }

The Natvie method is then delegated:

staticvoidobject, jint fd, jint wfd){#if defined(__linux__)    inotify_rm_watch((int)fd, (uint32_t)wfd);#endif}

The implementation here is very simple, which is to call the Inotify_rm_watch method to dismiss the relationship between the INotify instance and the watch instance.

So far we've figured out how the Fileobserver works, and to make it easier to understand, we use a simple diagram to describe the whole process:

Instructions for use

You have already understood the principle of fileobserver, and then we'll see how to use it. To implement file monitoring, we only need to inherit the Fileobserver class, and in onEvent() dealing with the related events, simply use the code to demonstrate:

 Public  class sdcardobserver extends fileobserver {     Public Sdcardobserver(String Path) {Super(path); }@Override     Public void onEvent(intI, String s) {Switch(i) { CaseFileobserver.all_events://All Events                 Break; CaseFileobserver.create://file is created                 Break; CaseFileobserver.delete://files are deleted                 Break; CaseFileobserver.modify://files are modified                 Break; }    }}
Precautions

Here we need to pay attention to two questions:
1. Carefully select the event you want to handle, otherwise it may cause a dead loop.
2. When you no longer need to listen, please remember to stop monitoring
3. Note that the Fileobserver object is garbage collected, from the above principle we know that the object is recycled will no longer trigger the event.

Talking about Fileobserver from the inotify mechanism

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.