Frequent Android Widget Updates - android

Greetings,
I'm trying to write a clock widget that displays Epoch time and I need it to update every second. Currently I'm doing this using a Service that uses a Handler:
public class EpochService extends Service {
private static final String TAG = "EpochService";
// the time
private static long mTime;
// ui components
private static RemoteViews mViews;
private static ComponentName mComponent;
private static AppWidgetManager mManager;
// handler
private static Handler mHandler = new Handler();
#Override
public void onStart(Intent intent, int id){
Log.i(TAG, "start");
// register the receiver
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(mIntentReceiver, filter);
// set up ui
mViews = new RemoteViews(this.getPackageName(), R.layout.main);
mManager = AppWidgetManager.getInstance(this);
mComponent = new ComponentName(this, Clock.class);
// start
mHandler.removeCallbacks(mEpochTimerTask);
mHandler.postDelayed(mEpochTimerTask, 1000);
}
#Override
public void onDestroy(){
// unregister receiver to prevent memory leak
mHandler.removeCallbacks(mEpochTimerTask);
unregisterReceiver(mIntentReceiver);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* The broadcast receiver - handles screen off events to stop drawing
*/
private static BroadcastReceiver mIntentReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
Log.i(TAG, "stop drawing");
mHandler.removeCallbacks(mEpochTimerTask);
}
else {
Log.i(TAG, "start drawing");
mHandler.removeCallbacks(mEpochTimerTask);
mHandler.postDelayed(mEpochTimerTask, 1000);
}
}
};
/**
* Run the Epoch Timer
*/
private static Runnable mEpochTimerTask = new Runnable(){
#Override
public void run() {
Log.i(TAG, "run");
mTime = System.currentTimeMillis() / 1000;
mViews.setTextViewText(R.id.time, Long.toString(mTime));
mManager.updateAppWidget(mComponent, mViews);
mHandler.postAtTime(mEpochTimerTask, SystemClock.uptimeMillis() + 1000);
}
};
}
The problem I'm having is that over time, my home screen appears to get noticeably laggy. I'm fairly new to Android so I'm not sure if what I'm doing is the correct approach or if there's a better way to achieve this (maybe with AsyncTask?). Is there a way to update a widget frequently without suffering from UI lag? If I need to post more code, please let me know. My widget simply starts and stops this service, and my appwidget-provder has android:updatePeriodMillis="0".
Thanks in advance for the help.

I'm trying to write a clock widget
that displays Epoch time and I need it
to update every second.
Please don't. Your users will attack you with pitchforks. Doing a remote procedure call every second will slow down the device considerably and will drain the battery.
If you want a clock on the home screen that updates every second, write a custom home screen, so it can all be done in a single process.
The problem I'm having is that over
time, my home screen appears to get
noticeably laggy.
This is to be expected.
Is there a way to update a widget
frequently without suffering from UI
lag?
No. The default update frequency is every 30 minutes, not every second.

Related

TimerTask stops firing in Service

I need to have a Service running in Android that stores a value to database every so often. How often is based on user preferences, and also if other events have happened, which can be as often as 30 seconds or up to 30 minutes.
This is not something that is hidden from the user and in fact the user should probably be aware its running. As such I think a foreground service is probably the best approach.
I have a foreground service running, with a TimerTask that calculates how often it needs to fire. That Service is 'sticky' so it should stick around and it low on resources the OS should start it back up after a while.
My problem is that the TimerTask seems to stop running after a while when the the app is backgrounded.
Here is my service:
public class TimerService extends Service {
private static final String LOG_NAME = TimerService.class.getName();
private Timer timer;
private final Handler timerHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
Notification notification = new NotificationCompat.Builder(this, "MY_APP_CHANNEL_ID")
.setContentTitle("My Timer Service")
.setContentText("Background timer task")
.setSmallIcon(R.drawable.timer)
.build();
startForeground(1, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
stopTimer();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void stopTimer() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
private void startTimer() {
stopTimer();
timer = new Timer();
long frequency = // calculate frequency
long delay = // calculate delay
timer.scheduleAtFixedRate(new MyTimerTask(), delay, frequency);
}
private void saveToDatabase() {
// Save some stuff to the database...
if (some condition) {
// might need to reschedule timer delay and frequency.
startTimer();
}
}
private class MyTimerTask extends TimerTask {
#Override
public void run() {
timerHandler.post(new Runnable() {
#Override
public void run() {
onTimerFire();
}
});
}
private void onTimerFire() {
try {
saveToDatabase();
} catch (Exception e) {
Log.e(LOG_NAME, "Error in onTimerFire", e);
}
}
}
}
Should this work? IE can I have a simple Timer in a foreground Service that fires continuously until that service is stopped? If so is there a bug in my code?
I chose a Timer to try to keep it simple, I only ever need one timer running and I wanted it to be able to reschedule easily. I do realize that I could try a Handler, ScheduledThreadPoolExecutor, or even an AlarmManager. I thought an AlarmManager might be overkill and a drain on resources if it is firing a ton. Not to mention rescheduling.
Why won’t it run in the background?
It is running in the background. It is not running when the device is asleep, as the CPU is powered down. In Android, "background" simply means "has no foreground UI" (activity, service with a foreground Notification).
I need to have a Service running in Android that stores a value to database every so often. How often is based on user preferences, and also if other events have happened, which can be as often as 30 seconds or up to 30 minutes.
What you want has not been practical on Android since 6.0.
I thought an AlarmManager might be overkill and a drain on resources if it is firing a ton.
That is true. However, the only way to get your existing code to work would be for you to acquire a partial WakeLock, thereby keeping the CPU running forever. This will be orders of magnitude worse for power than is AlarmManager. And AlarmManager is bad enough that each Android release, starting with 6.0, has made it progressively more difficult to use AlarmManager (or JobScheduler, or Timer and a wakelock) to do anything reliably.
You are going to need to learn what Android is and is not capable of with respect to background processing, then adjust your product plans accordingly. That subject is way too long for a Stack Overflow answer.
Here is a set of slides from a presentation that I delivered last year on this subject, with Android 8.0 in mind (use Space to advance to the next slide). You might also read:
My thoughts on Android P DP2, particularly "What’s New in the War on Background Processing"
This preview of one of my book chapters, where the section on Android 8.0 and its phase in "The War on Background Processing" happens to be included
Wakelock and doze mode
IMHO, writing an app that relies upon periodic background processing is a very risky venture nowadays.
You should use ScheduledExecutorService for the same. There can be many ways to schedule background task like Alarm Manager, JobDispatcher, Sync Adapter, Job Scheduler. I will suggest ScheduledExecutorService over them.
I have one good example of using ScheduledExecutorService in service. (Currently using in highly optimised location sync service)
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Created by KHEMRAJ on 1/29/2018.
*/
public class SyncService extends Service {
private Thread mThread;
ScheduledExecutorService worker;
private static final int SYNC_TIME = 60 * 1000; // 60 seconds
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
startSyncService();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
stopThread();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void stopThread() {
worker = null;
if (mThread != null && mThread.isAlive()) mThread.interrupt();
}
private void startSyncService() {
if (worker == null) worker = Executors.newSingleThreadScheduledExecutor();
if (mThread == null || !mThread.isAlive()) {
mThread = new Thread(new Runnable() {
#Override
public void run() {
saveToDb();
if (worker != null) {
worker.schedule(this, SYNC_TIME, TimeUnit.MILLISECONDS);
}
}
});
mThread.start();
}
}
private void saveToDb() {
// TODO: 5/15/2018
}
}

Boot Receiver having multiple task working strangely

My app needs to do some background task after boot completed and also in sleep mode.
So I'm using WakefulIntentService to avoid flow problem in sleep mode and it works well.
But when I'm trying to use the boot receiver for continuing data flow after boot completed it gives some strange error and also not working sometimes.
Here is my Boot Receiver class where I am trying to do multiple task.
public class OnBootReceiver extends BroadcastReceiver {
private static final int SERVICE_PERIOD = 300000; // 5 minutes currently
// private static final int HANDLER_PERIOD = 300000 * 12; // 1 hour
// currently
private static final String TAG = "OnBoot";
private Context mContext;
private int mInterval = 1000 * 60 * 60 * 2; // 2 hours by default, can be
// changed later
private Handler mHandler;
#Override
public void onReceive(Context context, Intent intent) {
mContext = context;
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, OnServiceReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 60000, SERVICE_PERIOD, pi);
mHandler = new Handler();
startRepeatingTask();
}
Runnable mStatusChecker = new Runnable() {
#SuppressWarnings("unchecked")
#Override
public void run() {
SharedPreferences mSharedPreferences = mContext
.getSharedPreferences(KEY_USER_ID, 0);
String UserId = mSharedPreferences.getString(KEY_USER_ID, "0");
new SyncValidater(mContext, UserId).execute();
mHandler.postDelayed(mStatusChecker, mInterval);
}
};
void startRepeatingTask() {
mStatusChecker.run();
}
void stopRepeatingTask() {
mHandler.removeCallbacks(mStatusChecker);
}
}
In this class I have written two tasks. the first one is starting another broadcast receiver which starts the wakeful service(same as github example). And the second one starts one handler which starts one AsyncTask class to do some server transaction in every two hours. But sometimes the handler starts in random time and sometimes stop.
So here I'm confusing that is my app requires two boot receivers ?
is there any other option to do these multiple tasks in the current receiver ?
please help. Thanks
And the second one starts one handler which starts one AsyncTask class to do some server transaction in every two hours
No, your second task will run once and probably never again, because your process is terminated in the meantime.
So here I'm confusing that is my app requires two boot receivers ?
No.
is there any other option to do these multiple tasks in the current receiver ?
Use two AlarmManager events, each with their own period. One is your current AlarmManager event. The other is one you create in support of your every-two-hour work to be done.

What kind of Service do I need? Custom IntentService?

I'm designing my first Android app.
This app consist in several Runnable that do some stuff. Initially I made this Runnable to be execute by a Thread (a Thread for each Runnable). Each Runnable is also Observable, so it can notify changes to Activity. User click on a start button, one or more Runnable starts, them do their job notifying gui during execution and then stops. All works fine.
First question: Is that approach the right one? In order to answer this question please keep reading.
I need two other things in my app:
to be sure that execution of my jobs doesn't stops, even if user goes away from my app to do something else;
to plan the execution of my Runnable that has to start and execute in background. Example: user decides that wants a "job" to be execute everyday at 16:00.
I've seen that I can do that with an AlarmManager and Service.
Second question: I need a Service that can manage several Runnable asynchronously, so when AlarmManager starts I ask this Service to do the requested job; I'll also modify the first part of application: instead of Thread I'll use this Service, so I can be sure that execution doesn't stop.
What kind of Service I need? IntentService can do this job?
It's right to proceed in this way? There is a better solution?
Can you give me some example of how I can implement all that?
I hope that I explained clearly my situation, otherwise I'll try to do it better.
Regards
First question: Is that approach the right one?
No, you should implement and run your Runnables in Threads in a Service.
An IntentService would be your best option if you don't require your Service to handle multiple requests simultaneously. If you start a Service it will keep running in the background even if the Activity that started it goes to the background or stops.
A Runnables can send a broadcast indicating a UI update is needed. The Activity should register a BroadcastReceiver to listen to the broadcast message and update the UI accordingly.
You can use an AlarmManager to schedule the execution of your jobs as you indicated. One way to do it is to schedule the AlarmManager to send a broadcast to be received by your IntentService which acts upon it by running the appropriate job.
Here is an example that combines all that:
Here is the IntentService
public class MyIntentService extends IntentService {
public static final String ACTION_START_JOB = "com.mycompany.myapplication.START_JOB";
public static final String ACTION_UPDATE_UI = "com.mycompany.myapplication.UPDATE_UI";
private final IBinder mBinder = new MyBinder();
// You can have as many Runnables as you want.
Runnable run = new Runnable() {
#Override
public void run() {
// Code to run in this Runnable.
// If the code needs to notify an Activity
// for a UI update, it will send a broadcast.
Intent intent = new Intent(ACTION_UPDATE_UI);
sendBroadcast(intent);
}
};
public MyIntentService() {
super("MyIntentService");
}
#Override
public void onCreate() {
// You need to register your BroadcastReceiver to listen
// to broadcasts made by the AlarmManager.
// The BroadcastReceiver will fire up your jobs when these
// broadcasts are received.
IntentFilter filter = new IntentFilter(ACTION_START_JOB);
registerReceiver(jobBroadcastReceiver, filter);
}
#Override
public void onDestroy() {
// You should unregister the BroadcastReceiver when
// the Service is destroyed because it's not needed
// any more.
unregisterReceiver(jobBroadcastReceiver);
}
/**
* This method is called every time you start this service from your
* Activity. You can Spawn as many threads with Runnables as you want here.
* Keep in mind that your system have limited resources though.
*/
#Override
protected void onHandleIntent(Intent intent) {
Intent intentFireUp = new Intent();
intentFireUp.setAction(ACTION_START_JOB);
PendingIntent pendingIntentFireUpRecording = PendingIntent
.getBroadcast(MyIntentService.this, 0, intentFireUp, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
int year = 2013, month = 5, day = 10, hourOfDay = 7, minute = 13, second = 0;
cal.set(year, month, day, hourOfDay, minute, second);
long startTime = cal.getTimeInMillis() + 5 * 60 * 1000; // starts 5
// minutes from
// now
long intervalMillis = 24 * 60 * 60 * 1000; // Repeat interval is 24
// hours (in milliseconds)
// This alarm will send a broadcast with the ACTION_START_JOB action
// daily
// starting at the given date above.
alarm.setRepeating(AlarmManager.RTC_WAKEUP, startTime, intervalMillis,
pendingIntentFireUpRecording);
// Here we spawn one Thread with a Runnable.
// You can spawn as many threads as you want.
// Don't overload your system though.
new Thread(run).run();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// Depending on your implementation, you may need to bind
// to this Service to run one of its methods or access
// some of its fields. In that case, you will need a Binder
// like this one.
public class MyBinder extends Binder {
MyIntentService getService() {
return MyIntentService.this;
}
}
// Spawns a Thread with Runnable run when a broadcast message is received.
// You may need different BroadcastReceivers that fire up different jobs.
BroadcastReceiver jobBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
new Thread(run).run();
}
};
}
And here is the Activity
public class MyActivity extends Activity {
Service mService;
boolean mBound = false;
ToggleButton mButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (ToggleButton) findViewById(R.id.recordStartStop);
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mButton.isChecked()) {
Intent intent = new Intent(MyActivity.this,
MyIntentService.class);
startService(intent);
}
}
});
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(MyIntentService.ACTION_UPDATE_UI);
registerReceiver(uiUpdateBroadcastReceiver, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(uiUpdateBroadcastReceiver);
}
BroadcastReceiver uiUpdateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Here goes the code to update your User Interface
}
};
ServiceConnection myServiceConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
// If you need
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyIntentService mService = ((MyBinder) service).getService();
mBound = true;
}
};
}
And don't forget to add the Service definition in your AndroidManifest.xml file:
<manifest ... >
...
<application ... >
<service android:name=".MyIntentService" />
...
</application>
</manifest>

Use alarmManager and service to perform schedule notification only during specific time period

I'm building an app that will trigger notifications at specific time-intervals during the users waking hours.
I have an alarmManager running inside of a service. The service is explicitly started via button click on the main activity and has the alarmManager executing notifications during specific time invervals. How would I go about stopping the notifications during certain hours of the day? I do not want these notification to be fired, for instance, while the user is sleeping.
My code that is currently firing notifications at user-set intervals is below (imports removed....this is long enough already):
public class FartSmackinChunks extends Service {
public Notification scheduleNotification;
public AlarmManager alarmScheduleManager;
public PendingIntent alarmScheduleIntent;
private Boolean autoUpdateBoolean = true;
private int intervalsGoneByInt = 0;
private Notification notification;
public static final int NOTIFICATION_ID = 1;
#Override
public void onCreate() {
// TODO: Actions to perform when service is created.
int icon = R.drawable.icon;
String tickerText = "INTERVAL FIRED";
long when = System.currentTimeMillis();
scheduleNotification = new Notification(icon, tickerText, when);
alarmScheduleManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
String ALARM_ACTION;
ALARM_ACTION = ScheduleAlarmReceiver.ACTION_REFRESH_SCHEDULE_ALARM;
Intent intentToFire = new Intent(ALARM_ACTION);
alarmScheduleIntent = PendingIntent.getBroadcast(this, 0, intentToFire,
0);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences mySharedPreferences =
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean autoUpdateBoolean =
mySharedPreferences.getBoolean("storedAutoUpdateBoolean", false);
String updateFreq =
mySharedPreferences.getString("storedInitialAverageTimeInterval", "00:00:00");
SimpleDateFormat dfInterval = new SimpleDateFormat("hh:mm:ss");
Date intervalTimeAsDateObject = null;
long updateFreqMilliLong;
try {
intervalTimeAsDateObject = dfInterval.parse(updateFreq);
} catch (ParseException e) {
e.printStackTrace();
}
updateFreqMilliLong = intervalTimeAsDateObject.getTime() - 18000000;
if (autoUpdateBoolean) {
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
long timetoRefresh = SystemClock.elapsedRealtime() +
updateFreqMilliLong;
alarmScheduleManager.setInexactRepeating(alarmType,
timetoRefresh, updateFreqMilliLong, alarmScheduleIntent);
notifications();
} else alarmScheduleManager.cancel(alarmScheduleIntent);
return Service.START_NOT_STICKY;
};
private void notifications() {
**notification stuff in here***
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Replace with service binding implementation.
return null;
}
#Override
public void onDestroy() {
this.alarmScheduleManager.cancel(alarmScheduleIntent);
}
}
.....and my broadcast receiver implementation here:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class ScheduleAlarmReceiver extends BroadcastReceiver {
public static final String ACTION_REFRESH_SCHEDULE_ALARM
= "com.application.ACTION_REFRESH_SCHEDULE_ALARM";
#Override
public void onReceive(Context context, Intent intent) {
Intent startIntent = new Intent(context, SmokerReducerService.class);
context.startService(startIntent);
}
}
I'm having a little difficulty wrapping my brain around how this should be implemented.
I was thinking to rework this code so that the alarmManager is fired at waketime and stopped at sleepTime, all while inside the service is a Timer that fires the notification method at specific intervals? Is there a better way to go about doing this?
Any input would be appreciated. I've been trying to work this out in my head for days now.
Thanks
EDIT:
#anyone who comes across this intending to use a Timer for daily notifications:
A timer which runs inside of a service will be paused by the runtime when the device is put to sleep (ie...the user puts the phone in standby). Therefor, using a Timer to fire notifications at specific time intervals won't work correctly within a service because when Android pauses the service, it also pauses the timer, which throws off the interval.
The correct way to do this is to use AlarmManager with an array of pending intents to set alarms at specific times during the day. This ensures that even if the phone is put in standby, the notifications (or whatever you want to happen at that time) will still be executed.
I was thinking to rework this code so that the alarmManager is fired at waketime and stopped at sleepTime, all while inside the service is a Timer that fires the notification method at specific intervals? Is there a better way to go about doing this?
To my mind, forget thinking of a 'better' way, it seems the only way. Using a timer to control (enable/disable) another timer isn't so strange and makes complete sense to me.

how to display progress of service in android

In my android application, I am using the tab view and so I have two tabs: parameters and results.
the user enters the various parameters on the first tab and then switches to the second tab to view the results.
i have a service that performs some long-running calculations. the user enters parameters on the first tab and hits 'calculate'. They can make adjustments and hit 'recalculate' and the service is updated with the new parameters.
As these calculations progress, I want the user to be able to switch to the results tab to view the results of the latest calculation. They would then view the results and be able to switch back to the parameters tab to make adjustments.
I can think of two approaches:
-register the 'results tab' with the service and when the service reaches a milestone, it calls directly to the 'results tab'.
-have a timer running in the 'results tab' and have it query against the bound service on a regular interval and update accordingly.
Do people have comments or recommendations for these two approaches?
AsyncTask has a publishProgress method that should make it really painless to push updates from your background task to the UI thread.
Using broadcast Receiver
public class Detail extends GDActivity {
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(DownloadService.CUSTOM_INTENT)) {
mProgressDialog.setProgress(intent.getFlags());
}
}
};
// Flag if receiver is registered
private boolean mReceiversRegistered = false;
// Define a handler and a broadcast receiver
private final Handler mHandler = new Handler();
#Override
protected void onResume() {
super.onResume();
// Register Sync Recievers
IntentFilter intentToReceiveFilter = new IntentFilter();
intentToReceiveFilter.addAction(DownloadService.CUSTOM_INTENT);
this.registerReceiver(mIntentReceiver, intentToReceiveFilter, null, mHandler);
mReceiversRegistered = true;
}
#Override
public void onPause() {
super.onPause();
// Make sure you unregister your receivers when you pause your activity
if(mReceiversRegistered) {
unregisterReceiver(mIntentReceiver);
mReceiversRegistered = false;
}
}
}
}
and the Sender
#Override
protected void onProgressUpdate(Integer... progress) {
Intent i = new Intent();
i.setAction(CUSTOM_INTENT);
i.setFlags(progress[0]);
ctx.sendBroadcast(i);
}

Categories

Resources