I have an Android app and I want to monitor a folder. I made a service(I want to monitor the folder non-stop, even if the user kill the app) and I put the folder's path in the extras of the Intent. In the service I have a FileObserver that should monitor my folder and trigger an event whenever a file is created inside the folder. The problem is that event is never triggered.
Am I doing something wrong? Thanks!
#Override
public int onStartCommand(Intent intent, int flags, int startId){
String mainFolder = intent.getStringExtra("mainFolder");
Toast.makeText(getApplicationContext(), "Service start! Main folder is: " + mainFolder, Toast.LENGTH_LONG).show();
FileObserver observer = new FileObserver(mainFolder) {
#Override
public void onEvent(int event, String path) {
if (path == null) {
return;
}
if (FileObserver.CREATE == event) {
Toast.makeText(getApplicationContext(), "This file was creted: " + path, Toast.LENGTH_LONG).show();
}
}
};
observer.startWatching();
return START_REDELIVER_INTENT;
}
Quoting the documentation for FileObserver:
Warning: If a FileObserver is garbage collected, it will stop sending events. To ensure you keep receiving events, you must keep a reference to the FileObserver instance from some other live object.
Your FileObserver instance is eligible for garbage collection as soon as onStartCommand() returns, as you are not holding it in a field, but in a local variable.
Also, bear in mind that services do not run forever.
Related
I have an application with only a service. Following is my code of the service, it is unable to call the device's build-in sms app.
public class smsservice extends Service {
private static final String TAG = "MyService";
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service created.");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("TAG", "Service started.");
try {
String sb = (String) intent.getSerializableExtra("dest1");
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("sms_body", sb);
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"SMS faild, please try again later!",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
Log.d("slog", "onDestroy()");
super.onDestroy();
}
}
I have include the permission in the manifest file
<uses-permission android:name="android.permission.SEND_SMS" />
Is there something I am missing or is it even possible
I have an application with only a service
First, I hope you have a plan for something to run your service. By default, nothing in your app will ever run.
Second, I hope that you can afford security guards. Since you have no activity, the only way anything will ever cause your service to run is if your service is exported. Unless you have some special tricks in mind, this means that any app can ask your service to send an SMS. If this gets exploited, your users may come after you, with guns and knives and so forth.
Third, there is no requirement for an Android device to support sending an SMS via ACTION_VIEW, let alone using some undocumented Intent extras. Use ACTION_SEND or ACTION_SENDTO.
it is unable to call the device's build-in sms app.
If you look at LogCat, I am guessing that you will see an error message mentioning that you need to add FLAG_ACTIVITY_NEW_TASK to the Intent to be able to start it from a service. You need to call addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) on the Intent, before calling startService().
That is because popping up an activity in the middle of whatever the user is doing usually is inappropriate. You do not know if the user is doing something with their device, when all of a sudden, your activity takes over the foreground. Users may also come after you with guns and knives for interrupting their game, their movie, their navigation instructions, etc. Hence, you should hire some security guards.
I have include the permission in the manifest file
That is for sending an SMS via SmsManager. You should not need it for ACTION_SEND or ACTION_SENDTO.
Pleas let me explain the scenario fist.
I have been writing a Dropbox like android app, which automatically upload photos to the server. When user turn on upload service, it keeps running at the background as long as it can.
The Service is used to upload photos, but I also added a Content Observer to watch camera event, because I need to upload the newly taken photo instantly. The Content Observer will detect camera event and upload the new photo. Until now everything is alright.
As I passed START_STICKY to the Service to make it keep running and restore itself after OS has enough memory. But the Content Observer does not. And that made the problem.
I need to ensure Content Observer functions as long as the service does, because the user may take camera photo at any time. The Content Observer has to detect the camera event at the background and upload it.
I glistered the Content Observer in onCreate() method under Service class, and unregisterd it under onDestory() method. see the code
#Override
public void onCreate() {
Log.d(DEBUG_TAG, "onCreate");
// bind transfer service
Intent bIntent = new Intent(this, TransferService.class);
bindService(bIntent, mConnection, Context.BIND_AUTO_CREATE);
Log.d(DEBUG_TAG, "try bind TransferService");
this.getApplicationContext()
.getContentResolver()
.registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false,
cameraUploadObserver);
}
#Override
public void onDestroy() {
Log.d(DEBUG_TAG, "onDestroy");
cancelUploadTasks();
/*this.getApplicationContext().getContentResolver()
.unregisterContentObserver(cameraUploadObserver);
cameraUploadObserver = null;*/
if (mTransferService != null) {
unbindService(mConnection);
mTransferService = null;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(DEBUG_TAG, "onStartCommand");
Log.d(DEBUG_TAG, "onStartCommand.intent: " + intent);
initializeCameraUploadPreference();
if (repoId != null && accountEmail != null) {
isCameraUpload = true;
account = new Account(accountServer, accountEmail, null, accountToken);
cUploadManager = new CameraUploadManager(account);
}
if (isCameraUpload) {
Log.d(DEBUG_TAG, "onStartCommand.startPhotoUploadTask");
ConcurrentAsyncTask.execute(new PhotoUploadTask());
}
return START_STICKY;
}
Below is how cameraUploadObserver was created and used
private CameraObserver cameraUploadObserver = new CameraObserver();
private class CameraObserver extends ContentObserver {
public CameraObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(DEBUG_TAG, "CameraOberser onChange");
ConcurrentAsyncTask.execute(new CameraEventReceiverTask());
}
}
So anyone can tell me why the service can keep running but the Content Observer, is there any code I need to add to keep the Content Observer running? any comments, advice, reference from you will be appreciated.
I have a BroadcastReceiver (Android 4.1) which must test for the existence of a file located in the external storage area. The receiver doesn't need to read or write to the file; just test for its existence.
I haven't found anything in the Android documentation which indicates that BroadcastReceivers cannot access external files, yet the code below always returns false.
The logcat output shows getExternalStorageState() returns "mounted" and I can access the file using an ordinary App, just not in the Receiver. No exceptions are thrown.
public class FileCheckReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean b = checkFile();
Log.d(TAG, "FileCheckReceiver: " + b);
}
boolean checkFile() {
String state = Environment.getExternalStorageState();
Log.d(TAG, "FileCheckReceiver, Environment.getExternalStorageState: " + state);
String name = "file.txt";
File f = new File(Environment.getExternalStorageDirectory() + "/" + name);
try
{
if (f.exists()) {
System.out.println(f.getCanonicalPath().toString());
return true;
}
}catch(Exception e) {
e.printStackTrace();
}
return false;
}
}
Are BroadcastReceivers stopped from accessing files?
A good approach would to be start a new Service from the BroadcastReceiver to handle tasks that you wish to perform.
Other things:
Make sure you have declared the receiver in manifest.xml
Also you need to consider the following as well.
As of Android 3.1 the Android system will by default exclude all BroadcastReceiver from receiving intents if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage Application).
This is an additional security features as the user can be sure that only the applications he started will receive broadcast intents.
I eventually discovered that System Apps (which my app was) do not have any access to files stored on external storage.
Moving my BroadcastReceiver to another (non-system) APK fixed the problem.
My application launches a service which spawns a thread. The thread does some polling every 5 seconds. Android can restart my service as it sees fit. I am trying to store the polling thread so when the service is restarted it can use the existing polling thread.
Here's the onStartCommand() method:
public int onStartCommand(Intent intent, int flags, int startId)
{
if(intent == null)
{
Log.d("screener", "onStartCommand was called with null intent. System must've killed and restarted service...?");
}
else
{
processToMonitor = intent.getStringExtra("com.screener.processToMonitor");
Log.d("screener", "onStartCommand was called");
}
if(pollThread == null)
{
poller = new Poller(this, wakeLock, processToMonitor);
pollThread = new Thread(poller);
pollThread.start();
Log.d("screenon", "polling thread was not already running. Going to start it");
}
else
{
Log.d("screener", "polling thread has already been running. Not going to restart it");
}
return START_STICKY;
}
It appears that when the passed Intent object is null, this signifies the service has been restartedm so I can successfully detect this scenario. The object I would like to store is poller (or pollerThread which encapsulates the poller). I have tried making it static which didn't work. I also overrode Application and stored an instance in there, but it is still not persisted.
So, how do I store my object when the service is restarted? Or, do I accept that Android cleans everything up and simply restart the thread?
Why do you want to store the thread? Let it "die" and once your service is restarted a new poller thread will start.
In my application, I create a service that aims to read something from sd card.
The service is created and started at boot time.
The problem is that although I am pretty sure that the directory exists, at the boot time, the service cannot find the directory.
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
Context context=getBaseContext();
File sdDir = new File(Environment.getExternalStorageDirectory()+"/temp/Data/");
if(!sdDir.exists()){
sdDir.mkdir();
Toast.makeText(this, "CAN'T FIND!", Toast.LENGTH_LONG).show();
}
}
This snippet above outputs
Service Started
CAN'T FIND!
At first, I thought that sd card might not be mounted at boot time and that's why the service can't find the directory. I am still not sure about that.
Anybody has an idea? What might be the problem?
Some devices take time to mount the SD card. It may not be available immediately after Boot time.
Just poll every few seconds until it becomes available.
Also try this:
public static boolean hasStorage(boolean requireWriteAccess) {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
} else if (!requireWriteAccess && Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
From Here