tags:

views:

211

answers:

1

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.

A: 

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.

CommonsWare
Are you saying there's no way to write a clock widget with a second hand without writing a new home screen? that doesn't seem right.
Jon
"Are you saying there's no way to write a clock widget with a second hand without writing a new home screen?" Correct. The app widget system is not designed for rapidly-changing data, and once per second definitely qualifies as "rapidly-changing".
CommonsWare