I am trying to monitor a folder on the users disk in the background, much like how you can monitor gallery changes with a JobScheduler
and contentobserver
. I want to do this for any specified directory. However, I cannot figure out how to receive a broadcast when the directory has any file changes.
2 Answers
Answers 1
There are several things you have to do in order create a system wide file observer.
Firstly
You have to create a service that will start up at boot and will always be running. In order for this to happen you have to create a BroadcastReceiver
, register it to receive ACTION_BOOT_COMPLETED
and the RECEIVE_BOOT_COMPLETED
permission to your Manifest
public class StartupReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent myIntent = new Intent(context, FileSystemObserverService.class); context.startService(myIntent); } }
In your Manifest
<manifest > <application > <service android:name=".FileSystemObserverService" android:enabled="true" android:exported="true" > </service> <!-- Declaring broadcast receiver for BOOT_COMPLETED event. --> <receiver android:name=".StartupReceiver" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> </application> <!-- Adding the permission --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> </manifest>
Secondly
The service has to implement the android.os.FileObserver
class. As android.os.FileObserver
only observes the path you send to it and does not observe the path's subdirectories, you have to extend it and add SingleFileObserver
observers to it. You also have to run the observation in another thread with priority set to low
public class FileSystemObserverService extends Service { @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { observe(); return super.onStartCommand(intent, flags, startId); } public File getInternalStoragePath(){ File parent=Environment.getExternalStorageDirectory().getParentFile(); File external=Environment.getExternalStorageDirectory(); File[] files =parent.listFiles(); File internal=null; if(files!=null){ for(int i=0;i<files.length;i++){ if(files[i].getName().toLowerCase().startsWith("sdcard")&&!files[i].equals(external)){ internal=files[i]; } } } return internal; } public File getExtenerStoragePath(){ return Environment.getExternalStorageDirectory(); } public void observe(){ Thread t=new Thread(new Runnable(){ @Override public void run() { //File[] listOfFiles = new File(path).listFiles(); File str=getInternalStoragePath(); if(str!=null){ internalPath=str.getAbsolutePath(); new Obsever(internalPath).startWatching(); } str=getExtenerStoragePath(); if(str!=null){ externalPath=str.getAbsolutePath(); new Obsever(externalPath).startWatching(); } }}); t.setPriority(Thread.MIN_PRIORITY); t.start(); } class Obsever extends FileObserver{ List<SingleFileObserver> mObservers; String mPath; int mMask; public Obsever(String path) { // TODO Auto-generated constructor stub this(path,ALL_EVENTS); } public Obsever(String path,int mask) { super(path,mask); mPath =path; mMask =mask; // TODO Auto-generated constructor stub } @Override public void startWatching() { // TODO Auto-generated method stub if(mObservers!=null) return; mObservers= new ArrayList<SingleFileObserver>(); Stack<String> stack= new Stack<String>(); stack.push(mPath); while(!stack.empty()){ String parent = stack.pop(); mObservers.add(new SingleFileObserver(parent,mMask)); File path = new File(parent); File[] files = path.listFiles(); if(files==null) continue; for(int i=0;i<files.length;++i){ if(files[i].isDirectory()&&!files[i].getName().equals(".")&&!files[i].getName().equals("..")){ stack.push(files[i].getPath()); } } } for(int i=0;i<mObservers.size();i++){ mObservers.get(i).startWatching(); } } @Override public void stopWatching() { // TODO Auto-generated method stub if(mObservers==null) return; for(int i=0;i<mObservers.size();++i){ mObservers.get(i).stopWatching(); } mObservers.clear(); mObservers=null; } @Override public void onEvent(int event, final String path) { if(event==FileObserver.OPEN){ //do whatever you want } else if(event==FileObserver.CREATE){ //do whatever you want } else if(event==FileObserver.DELETE_SELF||event==FileObserver.DELETE){ //do whatever you want } else if(event==FileObserver.MOVE_SELF||event==FileObserver.MOVED_FROM||event==FileObserver.MOVED_TO){ //do whatever you want } } private class SingleFileObserver extends FileObserver{ private String mPath; public SingleFileObserver(String path, int mask) { super(path, mask); // TODO Auto-generated constructor stub mPath=path; } @Override public void onEvent(int event, String path) { // TODO Auto-generated method stub String newPath=mPath+"/"+path; Obsever.this.onEvent(event, newPath); } } }
That's it With this code you'll be able to observe the entire file system, both the internal and external file systems
Answers 2
Just start a service that uses a Fileobserver to detect the file changes for the specified directory.
The service class:
public class FileObserverService extends Service { private FileObserver mFileObserver; @Override public int onStartCommand(Intent intent, int flags, int startId) { if((intent.hasExtra(INTENT_EXTRA_FILEPATH))) // we store the path of directory inside the intent that starts the service mFileObserver = new FileObserver(intent.getStringExtra(INTENT_EXTRA_FILEPATH)) { @Override public void onEvent(int event, String path) { // If an event happens we can do stuff inside here // for example we can send a broadcast message with the event-id Log.d("FILEOBSERVER_EVENT", "Event with id " + Integer.toHexString(event) + " happened") // event identifies the occured Event in hex } }; mFileObserver.startWatching(); // The FileObserver starts watching return Service.START_NOT_STICKY; } @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { //TODO for communication return IBinder implementation return null; } }
Start the service from somewhere inside your app with YOUR_FILEPATH/DIRECTORY you want to observe:
Intent intent = new Intent(this, FileObserverService.class); intent.putExtra(INTENT_EXTRA_FILEPATH, "YOUR_FILEPATH"); this.startService(intent);
0 comments:
Post a Comment