At the moment I have got a singleton class that extends service like this:
public class ServiceSingleton extends Service {
private static ServiceSingleton instance;
private static boolean serviceSt;
private static PrefValues preferences;
private static Context context;
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public static ServiceSingleton getInstance(Context cont) {
if (instance == null) {
context = cont;
// Some code
}
return instance;
}
So basically I run some methods in this class about every 30 minutes by using something like this:
private static void oneTasks() {
//task itself
}
private static void oneService() {
if (!serviceSt) {
serviceRunning = false;
return;
}
serviceRunning = true;
oneTasks();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
oneService();
}
}, (INTERVAL));
}
I also heard AlarmManager can do the same thing.
Anyway, my question is, If I am running periodical methods, which way to invoke methods is the best way(especially with the consideration of battery usage)?
At the moment I have got a singleton class that extends service like this
Yuck. Do not make a service be held indefinitely in a static data member.
So basically I run some methods in this class about every 30 minutes
You have not stated how you are doing that.
Anyway, my question is, If I am running periodical methods, which way to invoke methods is the best way(especially with the consideration of battery usage)?
If your objective is to only do this work when your process happens to be running for other reasons, you are welcome to use pretty much anything you want. I'd use ScheduledExecutorService.
If your objective is to do this work, even if your app is not running, AlarmManager covers that scenario. Team it with an IntentService, so that your process only needs to be in system RAM when it is actually doing work.
If your objective is to do this work, even if your app is not running, and even if the device falls asleep, you will need to use AlarmManager with a _WAKEUP alarm, coupled with either WakefulBroadcastReceiver, my WakefulIntentService, or the equivalent.
By using the Alarm Manager you can register a repeated alarm that will fire automatically every specific time, even if your application is closed. so it's a very efficient in term of battery usage.
Then inside the alarm's broadcast receiver you have to implement what you need. and you should consider creating a new thread or using IntentService class if your method will take more than a few seconds.
I know it's surely not the most elegant and best solution, but you could simply have a Thread with an infinite loop that had a SystemClock.sleep(1800000) at the end, so basically something like this:
final Thread buf_liberator = new Thread(
new Runnable() {
public void run() {
while (true) {
/* Your stuff */
SystemClock.sleep(1800000);
}
}
}
);
buf_liberator.setPriority(7);
buf_liberator.start();
Also you would need to have a stop condition inside the Thread as you can't stop it with the stop() method anymore.
You can also do it by CountDownTimer
CountDownTimer countDownTimer;
public void usingCountDownTimer() {
countDownTimer = new CountDownTimer(Long.MAX_VALUE, 10000) {
// This is called after every 10 sec interval.
public void onTick(long millisUntilFinished) {
setUi("Using count down timer");
}
public void onFinish() {
start();
}
}.start();
}
and onPause() method add
#Override
protected void onPause() {
super.onPause();
try {
countDownTimer.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
Related
I am creating an android app which needs a background service that fetches location and sends data to firebase every 20 seconds.The service has to start on button click and run continuously even when screen is turned off and should stop again on button click. At first , I tried using alarm Manager but it was not performing tasks at regular intervals. Next I tired using an Async Task and it was invoking a service which was performing task of sending data to firebase. But this approach, did not work on android 8+ versions. Then later on I used the similar approach but with JobIntent service and this approach worked well in android 7(appo) and even in android 8(lava) but in 8+ version(appo reno and mi) maybe due to custom OS , the service does not work if screen is turned off . I tried alternatives like workmanager but it did not work well in higher versions.
I created an activity named punch activity which has two buttons and code is as follows -
This button uses an async activity which calls service every 20 seconds.
#Override
public void onClick(View v) {
if (punchedIn){
Toast.makeText(PunchActivity.this, "Already PunchedIn",
Toast.LENGTH_LONG).show();
}
else {
timertask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
Intent intent = new Intent(PunchActivity.this, BackgroundService.class);
//sendBroadcast(intent);
BackgroundService.enqueueWork(PunchActivity.this, intent);
}
});
}
};
timer = new Timer();
timer.schedule(timertask, 0, 20000);
}
}
}};
This button stops the service
#Override
public void onClick(View v) {
punchedIn = false;
Toast.makeText(getApplicationContext(),"PUNCHED OUT",Toast.LENGTH_SHORT).show();
Log.d("Message","Process "+timer.toString());
if (timer != null) {
Log.d("Message","Process is killed");
timer.cancel();
timer = null;
wakeLock.release();
}
}
});```
The code for JobIntentService is as below
public class BackgroundService extends JobIntentService implements com.google.android.gms.location.LocationListener {
private static Context mContext;
private FusedLocationProviderClient fusedLocationProviderClient;
public static String latitude = "", longitude = "";
public static void enqueueWork(Context context, Intent work) {
mContext = context;
enqueueWork(context, BackgroundService.class, JOB_ID, work);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
//This task does the task of fetching location and sending data to firebase
YourTask();
}
}```
I have made use of power manager in punch activity but it did not work fine. So please give some suggestions or even rectify my approach if you find any issue, based on my use case . Any small clue could be helpful.
Thanks,
Vrashab
Just create a sub thread and request location in a loop like below:
private HandlerThread thread = new HandlerThread("location_thread");
private Handler locationHandler = new Handler(thread.getLoop())
private boolean sholdStop = false
private Runnable locationRunnable = new Runnable() {
while(!sholdStop) {
// location logic
...
Thread.sleep(20000);
}
});
// start to location per 20 seconds
public void startLocation() {
locationHandler.removeCallbacks(locationRunnable);
sholdStop = false;
locationHandler.post(locationRunnable);
}
public void stopLocation() {
sholdStop = true;
locationHandler.removeCallbacks(locationRunnable);
}
But if your app is killed by Android system, this code will be invalid. To solve this problem you might need some method to keep your app lives as long as possible when running background.
I have a service as MyService. In my service, I have a global variable: value which is controled by a function control_Value(). The function used to control the value of varialbe value as follows rules:
The time when calling the function is called initial time. At intial time, the value is set as One. After 3 seconds from initial time, the value is set to Zero. The value will maintain Zero until the function is called again.
Based on the rule above, I wrote the control_Value() as follows:
public void control_Value()(){
value="One";
try{
Thread.sleep(3000);
value="Zero";
}
catch{}
}
Do you think Thead.sleep(3000) is a good approach? If not, please give me a better solution. Note that, the above function worked well.
This is my service
public class MyService extends Service {
String value=null;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//TODO do something useful
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
//TODO for communication return IBinder implementation
return null;
}
#Subscribe
public void onSMSContentReceived(OnSMSReceiverEvent event) {
control_Value();
}
}
Update: The onSMSContentReceived is called automatically when a SMS come to phone.
This is solution using countdown timer from suggestion of TGMCians
//Global variable
private CountDownTimer mCountDownTimer;
//
#Override
protected void onDestroy() {
if(mCountDownTimer!=null){
mCountDownTimer.cancel();
}
super.onDestroy();
}
public void control_Value()(){
mCountDownTimer = new CountDownTimer(3000,1000) {
#Override
public void onTick(long millisUntilFinished) {
value="One";
}
#Override
public void onFinish() {
// Your stuff
value="Zero";
}
};
mCountDownTimer.start();
}
Do you think Thead.sleep(3000) is a good approach?
Never. Service run on application main thread without UI. Application will prompt ANR message in case you hold application main thread for certain seconds.
What to do
If you want to perform operation after certain seconds, then you can use CountDownTimer in your service which has methods onTick & onFinish where onTick hits on regular interval and onFinish hits when time is up.
i currently work on an app that needs a lot of battery in order to support background gps tracking. my experience shows that people just forget about the app runnning in the background when they dont really need the tracking anymore. therefore i setup some code that should close the application after 4 hours.
public class SelfDestructor {
private static SelfDestructor instance;
private final long IDLE_TIME_UNTIL_AUTO_DESTRUCT = 4 * 60 * 60 * 1000; // 4 hours
private Handler handler;
private Runnable closeApp = new Runnable() {
#Override
public void run() {
System.exit(0);
}
};
public static SelfDestructor getInstance() {
if (SelfDestructor.instance == null) {
SelfDestructor.instance = new SelfDestructor();
}
return SelfDestructor.instance;
}
public void keepAlive() {
if (handler == null) {
handler = new Handler();
}
handler.removeCallbacks(closeApp);
handler.postDelayed(closeApp, IDLE_TIME_UNTIL_AUTO_DESTRUCT);
}
}
now in my main activity i call keepAlive().
#Override
protected void onResume() {
super.onResume();
SelfDestructor.getInstance().keepAlive();
}
#Override
protected void onStart() {
super.onStart();
SelfDestructor.getInstance().keepAlive();
}
now if i set the time to an hours or so and debug the that functionality everything works fine. if i set the time to 4 hours the System.exit(0); is never called. i am assuming the app thread with the close callback is just put on hold by the android system after a while and therefore will not be executed anymore while gps will continue to run. any ideas how to properly get this to work?
handler and postDelayed are not suited for long timers. At most they should be used within a few seconds and personally I think I never used one for anything more than 2 seconds.
Said all that, Android have an appropriate class for "stuff that should happen after a long time", it's called AlarmManager: http://developer.android.com/reference/android/app/AlarmManager.html
you can get the references to the system service AlarmManager by calling Context.getSystemService(Context.ALARM_SERVICE)
and then set it by calling am.set(AlarmManager.ELAPSED_REALTIME, IDLE_TIME_UNTIL_AUTO_DESTRUCT, operation)
the operation is a PendingIntent to a BroadcastReceiver that you register in the AndroidManifest.xml via the <receiver> tag. Then you do the close application code inside this broadcast receiver.
Also I should add that it's NEVER good to call System.exit(0);, as this just destroy the VM without much of a warning. It's a better, more organised/structured shut down if you pass a command to the Service that is holding the GPS (I believe you're running a service), then this service will cancel the GPS request, and call stopSelf();
I'm not sure if this is the correct way to go about but I will try and explain what I want to do.
I have an Activity which creates a fragment called TemporaryFragment with a label. What I want to do is create and start a service with a Timer in it and that Timer then updates the time in that TextView.
The way I am thinking of going is somehow, when the Service is started, passing the TextView from the Activity to the Service and then the Service keeping a reference to it.
Another possible way is to make the Activity become a listener of the Service and then calling a method in the Service to update the TextView.
Any thoughts would be great and maybe some options.
Thanks in advance.
ADDITION
I'm sorry, I should also specify that I need this timer to run in the background. So when the application is sent to the background, I need the timer to carry on and only stop when I tell it to.
Service is not ideal for such minor task like this, moreover, Service can be run independently of activity. Also spawning new thread or using timer which introduces new thread into the application is not ideal for this relatively minor reason if you are thinking in the terms of mobile applications.
Instead use Handler in your fragment.
create handler in your fragment
private Handler mHandler = new Handler();
to execute your defined task call
mHandler.postDelayed(mUpdateTask, 1000);
or
mHandler.post(mUpdateTask);
and define your task in the fragment
private Runnable mUpdateTask = new Runnable() {
public void run() {
Toast.makeText(getActivity(), "hello world", Toast.LENGTH_SHORT).show();
mHandler.postDelayed(this, 1000);
}
};
If you are showing time-like information instead of countdown-like one, use
mHandler.removeCallbacks(mUpdateTimeTask);
in onPause() method to stop executing your task if the activity is not visible as updating UI isn't relevant and it saves battery (you start task again in onResume() method)
Basically, the idea behind the timer is eventually I am going to add some tracking into my application and therefore need it to continue running even if the application isn't in the foreground – Disco S2
Based on this comment I suggest you to use a local service which resides in the background, doing it's stuff (start a thread from Service#onStart), until it gets stopped by stopService(..).
Activities on the other hand may bind and unbind to that service (see: bindService(..)) to get notified about updates or to communicate with the service in any way.
I would use a more simple approach by using a Thread:
public class MainActivity extends Activity implements Callback {
private static final int MSG_UPDATE = 1;
private static final long INTERVAL = 1000; // in ms
private final Handler handler = new Handler(this);
private Thread worker;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE:
updateView();
return true;
}
return false;
}
private void updateView() {
// TODO tbd
}
#Override
protected void onStart() {
super.onStart();
// start background thread
worker = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
break;
}
// send message to activity thread
handler.sendEmptyMessage(MSG_UPDATE);
}
}
});
worker.start();
}
#Override
protected void onStop() {
super.onStop();
// stop background thread
worker.interrupt();
try {
worker.join();
} catch (InterruptedException e) {
}
worker = null;
}
}
You can use the TimerTask Class for this. Override the TimerTask.run() method and then add that TimerTask to Timer class.
Also check this question: controlling a task with timer and timertask
I have to run a bit of code in the background every one second, the code will call a webservice which searches a database and returns a value to the application. My question is which method would be the most effective to do this? I have read up on Timers, Threads, AsyncTask and Services and each seem to have their pros and cons. Please can someone tell me which would be the best to use considering execution time and battery life.
Thanks
Update:
I decided to use Aysnc task to run my code in the background while using a TimeTask to trigger the AsyncTask at regular intervals. This way the operation is destroyed when I leave that particular activity
You should use the service to do the background operation but in your case you want to run code in 1 sec here is the example of service using handler it call in every 1 sec.
public class YourService extends Service {
private static final String TAG = "Your Service";
private final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
}
};
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
// Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
}
#Override
public void onDestroy() {
super.onDestroy();
// Toast.makeText(this, "My Service Stopped", Toast.LENGTH_LONG).show();
handler.removeCallbacks(sendUpdatesToUI);
}
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
/// Any thing you want to do put the code here like web service procees it will run in ever 1 second
handler.postDelayed(this, 1000); // 1 seconds
}
};
#Override
public void onStart(Intent intent, int startid) {
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 1000);//1 second
Log.d(TAG, "onStart");
}
}
and service can't run every time android idle the service within 3 or 4 hr i suggested you to use the foreground service to use your process long running.
For operations like this I tend to use a Service component. for the task itself i use an AsyncTask which will wait a set time before it repeats itself (using a while loop).
You will have to create a new Thread so that the call don't lock up the device if the call takes longer than expected. The AsyncTask is an easy way to use multithreading, but it lacks the functionality of repeating tasks. I would say that you are best of either using a Timer or the newer ScheduledExecutorService.
If you chose to use the Timer you create a TimerTask that you can hand it. The ScheduledExecutorService takes a Runnable instead.
You might want to wrap the thread in a Service (The Service does not provide a new Thread), but this is not always necessary depending on your needs.
As suggested in comment, you can also use the Handler.postDelayed(). Although you still need to create a new thread and then call Looper.prepare() on it:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
(Code from Looper docs)
Also; calls to a webservice every second seems way too frequent, especially if the user is on a slow connection or there are data that needs to be transferred, try to reduce the calls as much as possible.
I think it's not only one solution, so it's up to you. You can try start thread with this run method:
private final int spleeptime = 1000;
public boolean running;
#Override
public void run() {
while (running) {
try {
int waited = 0;
while ((waited < spleeptime)) {
sleep(100);
waited += 100;
}
} catch (InterruptedException e) {
} finally {
// your code here
}
}
}