B4A Library FileObserver

Here's a new library for you all: FileObserver.

Documentation for the native Android FileObserver class can be found here: FileObserver | Android Developers.

Description:

Monitors files and fires events after files are accessed or changed by any process on the device.
Each FileObserver instance monitors a single file or directory.
If a directory is monitored, events will be triggered for all files and subdirectories (recursively) inside the monitored directory.

That's the official description, i found a page here where the author states:

One thing worth-mentioning is that the API documentation says “If a directory is monitored, events will be triggered for all files and subdirectories (recursively) inside the monitored directory”, but the FileObserver API is actually not recursive.

So that's something to watch for.

The library has just 5 methods:

EventTypeToString (EventType As Int) As String

Pass an EventType Int to this method to retrieve the EventType's String identifier.

Initialize (EventName As String, Path As String)

Initialize the FileObserver with an EventName and Path.
All FileObserver EventTypes will be triggered.

Initialize2 (EventName As String, Path As String, EventMask As Int)

Initialize the FileObserver with an EventName, Path and EventMask.
The EventMask will filter the EventTypes that will be triggered.

StartWatching

Start watching for events.

StopWatching

Stop watching for events.

FileObserver generates a single event:

OnEvent (EventType As Int, Path As String)

EventType is an Int which identifies the type of event, check the official documentation for possible EventTypes.
Path is the folder or file that the event applies to.
Path will be a partial path, it will not be the complete path to the folder or directory.
You will need to keep a reference to the Path value used in Initialize or Initialize2 and use that along with the event's Path value to create a full path.

If you call Initialize or Initialize2 with an EventName and the event handler Sub does not exist then the FileObserver will NOT be initialized.

Here's some example code to test the library:

B4X:
'   FileObserverDemo Activity module
Sub Process_Globals
End Sub

Sub Globals
   Dim FileObserver1 As FileObserver
End Sub

Sub Activity_Create(FirstTime As Boolean)

   FileObserver1.Initialize("FileObserver1", File.DirRootExternal&"/media")
   
   '   example of Initialize2 syntax
   '   FileObserver1.Initialize2("FileObserver1", File.DirRootExternal&"/media", FileObserver1.OPEN)
   
   FileObserver1.StartWatching
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
   '   FileObserver1.StopWatching
End Sub

Sub FileObserver1_OnEvent (EventType As Int, Path As String)

   Log("EventType="&FileObserver1.EventTypeToString(EventType)&", Path="&Path)
   
   Select EventType
      Case FileObserver1.ACCESS
         '   handle event
      Case FileObserver1.ATTRIB
         '   handle event
      Case FileObserver1.CLOSE_NOWRITE
         '   handle event
      Case FileObserver1.CLOSE_WRITE
         '   handle event
      Case FileObserver1.CREATE
         '   handle event
      Case FileObserver1.DELETE
         '   handle event
      Case FileObserver1.DELETE_SELF
         '   handle event
      Case FileObserver1.MODIFY
         '   handle event
      Case FileObserver1.MOVE_SELF
         '   handle event
      Case FileObserver1.MOVED_FROM
         '   handle event
      Case FileObserver1.MOVED_TO
         '   handle event
      Case FileObserver1.OPEN
         '   handle event
   End Select
End Sub

Testing with an Activity is tricky...
You need to trigger an event in the monitored folder without stopping the FileObserver from listening.
I found that running this Activity then pressing the Home key followed by using a file explorer to navigate to my SD card's media folder enabled me to trigger an event.

Version 1.00 is attached along with the example code.

Martin.
 

Attachments

  • FileObserver_v1_00.zip
    10.2 KB · Views: 733

Jost aus Soest

Active Member
Licensed User
Longtime User
Thank you for sharing this interesting library!

BTW:
Does someone know, how many ressources (processing time) does it cost to use such a service?
Is the OS polling the directories all the time (which would be very expansive)?
 

thedesolatesoul

Expert
Licensed User
Longtime User
HotShoe was trying something like this.
Another restriction worth mentioning is that the FileObserver needs to stay alive for it to work.
Do you think a foreground service should be used for this? What if the service dies, then we don't detect a change?

Thanks a lot for your work warwound :)


Sent from my GT-I9000 using Tapatalk
 

warwound

Expert
Licensed User
Longtime User
HotShoe was trying something like this.

Yes i kind of hijacked it earlier - got carried away putting some example code together and before i knew it had a working library!!

I'm not sure about methods to keep the FileObserver alive though.

The Android documentation implies that as long as there is a reference somewhere to the FileObserver then it will remain active.

Martin.
 

Rusty

Well-Known Member
Licensed User
Longtime User
I am trying the FileObserver example above (verbatim, with folder location changed) and am getting this error:
sending message to waiting queue (fileobserver1_onevent)
Any suggestions as to what I've done wrong?
Rusty
 

Rusty

Well-Known Member
Licensed User
Longtime User
Thanks Erel,
I moved the code to the Starter Service (made it a sticky service) and any time I browse the folder being watched (/DCIM/Camera), it fires Open, access, close-NOWRITE events properly.
But when I take a photo that ends up in that folder, there is no event that fires. I would expect the CREATE event to fire or at least some other event.
B4X:
#Region  Service Attributes
    #StartAtBoot: False
    #ExcludeFromLibrary: True
    #StartCommandReturnValue: android.app.Service.START_STICKY
#End Region

Sub Process_Globals
    Dim FileObserver1 As FileObserver
End Sub

Sub Service_Create
    FileObserver1.Initialize("FileObserver1", File.DirRootExternal & "/DCIM/Camera")    '"/survey/data/binary/tempbinary")        '"/storage/ABDB-17FC/DCIM/Camera")
    '    Dim f As List = File.ListFiles(File.DirRootExternal & "/DCIM/Camera")
    '    For Each fl In f
    '        Log("File: " & File.DirRootExternal & "/DCIM/Camera" & fl)     '<---<<< this will fire events
    '    Next
    '    example of Initialize2 syntax
    '    FileObserver1.Initialize2("FileObserver1", File.DirRootExternal&"/media", FileObserver1.OPEN)
 
    FileObserver1.StartWatching
End Sub

Sub Service_Start (StartingIntent As Intent)
    StartServiceAt("", DateTime.Now + 1000, False)
End Sub

Sub FileObserver1_OnEvent(EventType As Int, Path As String)

    Log("EventType=" & FileObserver1.EventTypeToString(EventType) & ", Path=" & Path)
 
    Select EventType
        Case FileObserver1.ACCESS
            '    handle event
        Case FileObserver1.ATTRIB
            '    handle event
        Case FileObserver1.CLOSE_NOWRITE
            '    handle event
        Case FileObserver1.CLOSE_WRITE
            '    handle event
        Case FileObserver1.CREATE
            Log("                File Created " & Path)
            '    handle event
        Case FileObserver1.DELETE
            '    handle event
        Case FileObserver1.DELETE_SELF
            '    handle event
        Case FileObserver1.MODIFY
            '    handle event
        Case FileObserver1.MOVE_SELF
            '    handle event
        Case FileObserver1.MOVED_FROM
            '    handle event
        Case FileObserver1.MOVED_TO
            '    handle event
        Case FileObserver1.OPEN
            '    handle event
    End Select
End Sub

Sub Service_TaskRemoved
    'This event will be raised when the user removes the app from the recent apps list.
End Sub

'Return true to allow the OS default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub Service_Destroy
'    FileObserver1.StopWatching
End Sub

Any suggestions?
Rusty

Additional Note: I changed an application that I have that uses the CameraExClass the allows me to put the photo in a NON /DCIM/Camera location and then watched the new location. When the camera creates a new photo, it puts it there and the fileobserver fires:
picture taken
save photo /storage/emulated/0/survey/data/binary/tempbinary ThisPhoto.jpg
** Service (starter) Start **
** Service (starter) Create **
** Service (starter) Start **
File progress: - 0%
EventType=OPEN, Path=ThisPhoto.jpg
EventType=ACCESS, Path=ThisPhoto.jpg
EventType=ACCESS, Path=ThisPhoto.jpg
EventType=CLOSE_NOWRITE, Path=ThisPhoto.jpg
** Service (starter) Start **
EventType=MOVED_FROM, Path=ThisPhoto.jpg '<---<<< my application move the file
EventType=OPEN, Path=null
EventType=ACCESS, Path=null
EventType=CLOSE_NOWRITE, Path=null
** Service (starter) Start **
Is the DCIM/Camera folder special? Is there a reason the same code pointed at DCIM doesn't fire these events?
Your help is appreciated!
Rusty
 
Last edited:

Claudio Oliveira

Active Member
Licensed User
Longtime User
I put this code on Starter service:
B4X:
Sub Service_Create

    FileObserver1.Initialize2("FileObserver1", File.DirRootExternal&"/DCIM/Camera",FileObserver1.CREATE+FileObserver1.DELETE )

    '    example of Initialize2 syntax
    '    FileObserver1.Initialize2("FileObserver1", File.DirRootExternal&"/media", FileObserver1.OPEN)
   
    FileObserver1.StartWatching

End Sub

Sub Service_Start (StartingIntent As Intent)

End Sub

Sub Service_Destroy

End Sub
Sub FileObserver1_OnEvent (EventType As Int, Path As String)

    Log("EventType="&FileObserver1.EventTypeToString(EventType)&", Path="&Path)
   
    Select EventType
        Case FileObserver1.ACCESS
            '    handle event
        Case FileObserver1.ATTRIB
            '    handle event
        Case FileObserver1.CLOSE_NOWRITE
            '    handle event
        Case FileObserver1.CLOSE_WRITE
            '    handle event
        Case FileObserver1.CREATE
            '    handle event
        Case FileObserver1.DELETE
            '    handle event
        Case FileObserver1.DELETE_SELF
            '    handle event
        Case FileObserver1.MODIFY
            '    handle event
        Case FileObserver1.MOVE_SELF
            '    handle event
        Case FileObserver1.MOVED_FROM
            '    handle event
        Case FileObserver1.MOVED_TO
            '    handle event
        Case FileObserver1.OPEN
            '    handle event
    End Select
End Sub

... and got these results, which looks ok for me...

B4X:
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Service (starter) Destroy **
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
EventType=CREATE, Path=IMG_20170402_143045966.mp4
EventType=CREATE, Path=IMG_20170402_143045966.met
EventType=DELETE, Path=IMG_20170402_143045966.mp4
EventType=DELETE, Path=IMG_20170402_143045966.met
EventType=CREATE, Path=IMG_20170402_143045966.jpg

Running Android 7.0 on a Motorola Moto Z device.
 
Top