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