Android - Calling a system service in fixed intervals from another service - android

As the question implies, I am wondering how I could write a thread that would call a system service and then wait a certain amount of time before calling said system service's function that calls back to onReceive from a registered BroadcastReceiver.
In other words, I am trying to call the Wifi scanning service (registering a BroadcastReceiver with IntentFilters) within my custom service so I can get the current SSID's available. I know what I will end up doing with the received data, which is not relevant to this question. However, I will need to wait a certain amount of time before calling startScan again within onReceive, and that is where I am trying to determine the best course of action.
I managed to try calling the wifi scanner in this fashion within my thread:
private Object _lock = new Object();
private final long SLEEP_TIME = 15000; //Scan every 15 secs
private final long WIFI_SCAN_TIMEOUT = 60000; //One minute timeout for getting called back, otherwise, initiate a new scan.
#Override
public void run() {
while(running){
//Start a new scan;
wifiSearchComplete = false;
_wifiMan.startScan();
while(!wifiSearchComplete){
synchronized(_lock){
try{
_lock.wait(WIFI_SCAN_TIMEOUT);
} catch (InterruptedException ie){
Log.d(TAG, TAG+".run() caught " + ie.getMessage() +" when trying to sleep for " + (WIFI_SCAN_TIMEOUT/1000) +"secs.");
Thread.currentThread().interrupt();
}
}
if(!wifiSearchComplete){
synchronized(_lock){
//Try scanning again since we didn't get called back at all;
_lock.notify();
}
}
}
}
}
public boolean isRunning(){
return running;
}
public void stop(){
synchronized(_lock){
running = false;
//unregister receivers and cleanup
_lock.notify();
}
}
#Override
public void onReceive(Context context, Intent intent) {
synchronized(_lock){
wifiSearchComplete = true;
//iterate through our SSID's
try{
_lock.wait(SLEEP_TIME);
} catch (InterruptedException ie){
Log.d(TAG, TAG+".onReceive() caught " + ie.getMessage() +" when trying to sleep for " + (SLEEP_TIME/1000) +"secs.");
Thread.currentThread().interrupt();
}
_lock.notify();
}
}
However, even though it waits every 15 seconds before it scans again, when trying to exit my test activity (calling onDestroy) it blocks the main thread for the sleep time, before it unbinds the service. In other words, is this the appropriate way of trying to accomplish what I want to do without blocking, or do I have to simply create a BroadcastReceiver and call Thread.sleep at the end of onReceive before calling starting a new scan?

You should implement an IntentService. In your implementation override onHandleIntent() to do you WiFi scan.
Next, use the AlarmManager to schedule sending an Intent to your IntentService at some interval. Make up your own action name: "diago.intent.scan_wifi" or something like that. If you use one of the "inexact repeating intervals" (such as 15min) then the Android OS will schedule all the other services at the same time to minimize the number of times the phone must wake from sleep mode.
Finally, Implement a BroadcastReceiver to respond to ACTION_BOOT_COMPLETED and call your code to schedule the AlarmManager. This will start your service on boot.
This way when the AlarmManager sends the intent, your service will be loaded, execute the scan and then exit.

Related

Killing a service

I was reading about services in Android and especially i came down to this
While an app is in the foreground, it can create and run both
foreground and background services freely. When an app goes into the
background, it has a window of several minutes in which it is still
allowed to create and use services. At the end of that window, the app
is considered to be idle. At this time, the system stops the app's
background services, just as if the app had called the services'
Service.stopSelf() methods.
In the code below, when the app goes to background after one minute or so the services gets destroy but the thread still executes.
So what is the point of killing a service? The process/thread is still being execute.
Why killing a service in the first place? What if i want to execute a download process and NOT wanted to be a foreground process?
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
Log.d("sssssssss",msg.toString()+"sssssssssss");
while(true){
Log.d("sssssssss","sssssssssss");
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
//stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
System is letting You perform a clean finish of the Service by triggering its destroy method. Your thread won't run forever, I think 30 min is hard limit before app process is killed.
This is new policy since android O to preserve battery life and improve performance. A lot of developers performed heavy operations in background (sockets open non-stop, periodic sensor readings etc.) and without foreground notification users were unaware of why their devices were sluggish and had poor battery uptime.
Read more on Background Execution Limits doc.

Android JobIntentService seems to be restarted

I have a broadcast receiver that listens for power connection events. Whenever the device is connected to power, I attempt to transfer files from the APP to a Server in a machine running Ubuntu. The files are transferred over Bluetooth. Since the transfer of files is important, if for any reason the transfer has an error, or the connection is not successful in a first attempt, I retry it up to 6 times allowing 3 minutes between attempt.
In the beginning, I was using an asynctask which was simply maintained alive as long as we still have retries available and the file transfer has not been successfully done. I read, that having an asynctask in a broadcast receiver is not a good practice which makes total sense, especially since I'm forcing the task to run for long periods of time. Therefore, I decided to change to a JobIntentService such that every time a power connection event was captured by the receiver, I would issue the job that will transfer files to my computer. Within the job, right after the file transfer is finished or failed, I would set an alarm that will send a pending intent to the broadcast and call the job again.
I was running this and I have noticed that (as different from before) I've gotten too many "Connection reset by peer" errors during the transfer, which makes me wonder if the Job is being stopped before its completed or something like that?. Those errors used not to happen in my previous implementation. Then, I also noticed that for some reason the OS seems to have launched the JobIntentService again by itself (there was no event that launched it) which caused inconsistencies on my code and caused me to lose some files (I'm not supposed to allow multiple instances of this job running at the same time)
My question is, why do you think the service was restarted? is it possible for the JobIntentService to be finished and restarted by the OS during the BT transfer? The files are heavy so they take several minutes to transfer from the app to the machine. I was thinking of trying a foreground service instead of the JobIntent and having a notification for the service or going back to my previous implementation.
Any suggestions?
This is how I call the Intent Job.
FileTransferJob.isJobAlreadyRunning = true;
Intent intent = new Intent(context, FileTransferJob.class);
intent.putExtra(TRANSFER_DATA_RETRIES, retries);
FileTransferJob.enqueueWork(context,intent);
This is the JobIntentService class
public class FileTransferJob extends JobIntentService {
/**
* Unique job ID for this service.
*/
public static boolean isJobAlreadyRunning = false; //This flag will remain true as soon as this JOB is called and as long as retries are still available
public static final int JOB_ID = 1000;
public static int MAX_NUM_OF_RETRIES = 6;//How many times are we going to retry to send the data
private int MINUTES_TO_WAIT = 3; //The minutes we wait between each attempt
public String TAG = "FileTransferJob";
/**
* Convenience method for enqueuing work in to this service.
*/
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, FileTransferJob.class, JOB_ID, work);
}
#Override
protected void onHandleWork(Intent intent) {
int retriesRemaining = intent.getIntExtra(TRANSFER_DATA_RETRIES,1); //Get the number of retries we have. Default to 1 (this one)
Log.d(TAG, "onHandleWork: About to attempt transfer with remaining retries " + String.valueOf(retriesRemaining));
try {
BluetoothFileTransfer btio = new BluetoothFileTransfer();
Log.d(TAG, "onHandleWork: About to send data over Bluetooth");
btio.sendData(FileTransferJob.this.getApplicationContext());
FileTransferJob.isJobAlreadyRunning = false; //Success, then this is no longer running
Log.d(TAG, "onHandleWork: The data has been sent over Bluetooth");
}catch (Exception e){
Log.d(TAG, "onHandleWork: There was a problem with the BT transfer: " + e.getMessage());
retriesRemaining--; //We reduce the number of retries we have
//If no more retries available, simply do nothing
if (retriesRemaining > 0) {
Log.d(TAG, "onHandleWork: Setting up alarm. Retries ramaining: " + String.valueOf(retriesRemaining));
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent alarmIntent = new Intent(this.getApplicationContext(), DataCollectReceiver.class);
alarmIntent.setAction(TRANSFER_DATA);
alarmIntent.putExtra(TRANSFER_DATA_RETRIES, retriesRemaining);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast( this.getApplicationContext(), PENDING_INTENT_CODE_FILE_TRANSFER_JOB, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
int totalTime = MINUTES_TO_WAIT*60*1000;
if(alarmManager != null){
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + totalTime,
alarmPendingIntent);
Log.d(TAG, "onHandleWork: Alarm is set, waiting " + String.valueOf(totalTime) + " minutes for next attempt...");
}else{
Log.d(TAG, "onHandleWork: Alarm could not be set. Alarm manager is NULL");
}
}else{
Log.d(TAG, "onHandleWork: There are no more retries");
FileTransferJob.isJobAlreadyRunning = false;
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: The file transfer JOB has finished");
}
}
The logcat. The highlighted section shows what I believe is the OS creating a new instance of the JobService and running it.
Let me try to answer it as i have noticed this behavior. The JobIntentService/JobService/Worker will run only for 10 mins after that they will be stopped and you can get a call back on onStopJob/onStopCurrentWork in case of JobService/JobIntentService and OnStopped in case of Worker.
Though the android document has explained this behavior for Worker only but JobService/JobIntentServie both behaves the same way
A Worker is given a maximum of ten minutes to finish its execution and return a ListenableWorker.Result. After this time has expired, the Worker will be signalled to stop.
Hence i can assume that your task is not finished within 10 mins and Android is destroying the JobIntentService.
Now the thing is that All of these Jobservice/JobIntentService/Worker are started again (If stopped prematurely) after the exponential backoff time i.e. 30secs , 1 min, 2 mins,4 mins...
Although the weird part is that the old thread which died after running 10 mins started as explained but as the call back comes again on HandleWork it starts another thread again which duplicates the work done by the thread and that is why i think you see inconsistencies.
The suggestion is that you break your work in such a way that can be finished withing the 10 mins window. Or We can wait for Google team to fix this.

Does stopService kill a Android service or does it wait untill it's idle?

I currently have a service that processes some stuff, and it is started with startService.
I was wondering, can I call `stopService immediately after I start the service and expect it to stop the service after the processing is done?
Or does Android kill the service when I call that command?
One hopes that "processes some stuff" is done in a background thread, assuming that it will take more than a couple of milliseconds.
Android is largely oblivious to such a background thread. stopService() will trigger onDestroy() of the service, and the service will go away. The thread, however, will continue to run, until it terminates on its own, or until the process is terminated.
can I call `stopService immediately after I start the service and expect it to stop the service after the processing is done?
Only if "the processing" is done on the main application thread (e.g., in the body of onStartCommand()), which, again, is not a good idea if such work will take more than a couple of milliseconds. And, if that indeed is the case, there's no good reason for having a service in the first place.
If you want to have a service that:
Has a background thread, and
Automatically shuts down when the work is complete (avoiding the need for stopService())
then use an IntentService.
Android can't kill just a single Service. All it can do is to kill the whole process and everything running within. Most apps will have just 1 process so this usually means Android kills everything or nothing. Most of the times nothing.
The lifecycle of a Service or Activity tells Android whether it may kill the process safely or not. The Processes and Threads describes the order in which processes are kill if there is demand for memory.
It is important to know that a Thread started from a Service / Activity it is not affected at all by onDestroy etc. It just keeps running. Android simply does not know about that thread and won't stop it for you.
That means if you want to do some background processing you have link the lifecycle of such threads to the lifecycle of your Activity / Service or Android may just kill the process and thus your thread.
Quick example of a Service that prints to logcat every second while running. Not based on IntentService since that's more or less intended for tasks with an end.
public class MyService extends Service {
public static void start(Context context) {
context.startService(new Intent(context, MyService.class));
}
public static void stop(Context context) {
context.stopService(new Intent(context, MyService.class));
}
private final ExecutorService mBackgroundThread = Executors.newSingleThreadExecutor();
private Future<?> mRunningTask;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// startService -> start thread.
if (mRunningTask == null) {
// prevents task from being submitted multiple times.
// actually not necessary when using a single thread executor.
mRunningTask = mBackgroundThread.submit(mRunnable);
}
return START_STICKY;
}
private Runnable mRunnable = new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
try {
// Do something
Log.d("Service", "I'm alive");
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.d("Service", "Got interrupted", e);
Thread.currentThread().interrupt();
}
}
}
};
#Override
public void onDestroy() {
// stopService > kill thread.
mBackgroundThread.shutdownNow();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
According to the documentation:
stopService(Intent service)
Request that a given application service be stopped. If the service is
not running, nothing happens. Otherwise it is stopped. Note that calls
to startService() are not counted -- this stops the service no matter
how many times it was started.
Note that if a stopped service still has ServiceConnection objects
bound to it with the BIND_AUTO_CREATE set, it will not be destroyed
until all of these bindings are removed. See the Service documentation
for more details on a service's lifecycle.

Android: Developing apps that run in the 'background', how?

I am used to developing standalone applications, ones that you click on, it runs, and when you are done, you exit.
I am now interested in tackling a new type (not sure if that's the right word) of app, and was wondering how I should go about it. I am not sure what to research, and would appreciate your advice to help me get the ball rolling. I'll give you an idea about what I have in mind.
My app would need to perform a special action in the dialer. When the user dials a number and is in the middle of a call, I would like the user to be able to press the Menu key, and find an option to scroll through all their contacts (either the stock app, or my own list which I grab from the contacts stored in the phone), and select one. Upon selection, that contact's number is pasted into the dialer (keep in mind, in the middle of a call).
I certainly don't expect an answer telling me how to do this exactly, I just need some guidance as I have never written an app of this nature before. On top of that, is it even possible to do what I want to do?
Thank you.
You need to go through Android Service or IntentService. A Service is an application component that can perform long-running operations in the background and does not provide a user interface(UI).
The following example is taken from android blog which is an implementation of the Service class
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
On the other hand, The same thing can be achieved using IntentService, which is a base class for Services that handle asynchronous requests on demand.
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
You can also go through SO post https://stackoverflow.com/a/4353653/432903
If your app isn't mainly written in javascript/webview/phonegap, then all you have to do is look at the Service class. That class and the linked documents tell you everything you need to know.
maybe you can use an IntentFilter so you can get a system notify when the user uses a dialer.
and you should learn the Service component which can work in background in android.

loop in IntentService

I have an infinite loop in my IntentService to update my view once every 30 seconds based on the input from the main activity.
public class IntentServiceTest extends IntentService {
String Tag = "IntentServiceTest";
String ACTION_RCV_MESSAGE = "com.jsouptest8.intent.action.MESSAGE";
public IntentServiceTest(){
super("IntentServiceTest");
Log.d(Tag, "IntentServiceTest constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
Log.d(Tag, "in onHandleIntent");
String url = intent.getStringExtra("URL");
Document doc;
int i=0;
try{
while(true){
Log.d(Tag, "entered try block...");
Log.d(Tag, "url = "+url);
doc = Jsoup.connect(url)
.get();
Log.d(Tag, "past Jsoup.connect");
Element data = doc.select("table").get(1).attr("bgcolor", "#f4f36f");
Log.d(Tag, data.toString());
Log.d(Tag, data.text());
Log.d(Tag, "creating intent...");
Intent broadcastIntent = new Intent();
Log.d(Tag, "setting action...");
broadcastIntent.setAction(ACTION_RCV_MESSAGE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("OUTPUT", data.toString());
Log.d(Tag, "sending broadcast: "+(i++));
sendBroadcast(broadcastIntent);
Thread.sleep(30*1000);
}
}
catch(StackOverflowError e){
Log.d(Tag, "in StackOverflowError block...");
Log.d(Tag, "creating intent...");
Intent broadcastIntent = new Intent();
Log.d(Tag, "setting action...");
broadcastIntent.setAction(ACTION_RCV_MESSAGE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("OUTPUT", "系統忙線中, 請稍後再試");
Log.d(Tag, "sending broadcast...");
sendBroadcast(broadcastIntent);
}
catch(Exception e){
Log.d(Tag, "in catch Exception block...");
onHandleIntent(intent);
}
}
}
The problem is, I am stuck in this loop. Even if I kill the main activity and then return to it to enter a new input and the IntentService still returns based on the old input.
I need to know how I can update myself from the URL every 30 second without getting stuck. Thanks!
An IntentService is meant to finish of a task and return. It does this task in a new thread. Do not use while loop in IntentService. Your IntentService will get killed after sometime. I am telling this from personal experience. I tried using a while loop in it. And at the end of the while loop I used sleep(60000) i.e 1 minute. But I found that my IntentService was killed after sometime.
I would recommend you not to use an AlarmManager for 30 seconds, as some have siggested. Because 30 seconds is too short. it will drain the battery. For AlarmManager use a minimum 1 minute with RTC.
If you still want it to be 30 seconds, use a service. In the service use your logic. But do that in a separate thread i.e spawn a new thread in your Service and used while loop there and sleep(). And do not forget to use startForeGround. This reduces the probabilty of android killing your service greatly.
Using a while statement inside an IntentService, or any kind of Service for that matter is a bad idea. It is especially a bad idea inside an IntentService because the IntentService is supposed to finish a task and then get terminated automatically, you are in essence defeating the whole purpose of using an IntentService.
I would recommend to remove the loop in the IntentService and to use an alarm to wake up the IntentService every 30 seconds. That way, your service gets called every 30 seconds for sure and for the time that it is not processing, it can actually go back to sleep. Moreover, to handle cases where a new call to the IntentService is received while the IntentService is servicing an older request, you can add code to the onStartCommand method of your IntentService to see if the call should be enqueued for processing or ignored altogether.
Set an alarm using this method:
public void setRepeating (int type, long triggerAtMillis, long
intervalMillis, PendingIntent operation)
Link: http://goo.gl/E9e6
For a more efficient approach, use setInexactRepeating (but that does not guarantee a 30 second wakeup)
PS. We don't normally override the onStartCommand of an IntentService but it can be done if your app really that functionality.
in this link you'll find a service that updates itself using a timer
Keep Service running
If your comfortable with the while loop just write an if statement that exists the loop
if(thisIsTrue)
{
break; // this will exit the loop!
}
It would be better that you keep the loop or timer or any such running task in the MainActivity itself and execute IntentService everytime. Because IntentService will perform task and finish itself everytime or queue the task to be delivered further.
From the Docs -
IntentService will receive the Intents, launch a worker thread, and
stop the service as appropriate.
It uses work queue processor pattern to maintain the task.

Categories

Resources