views:

78

answers:

2

hi there!

i'm currently working on an app for the android os that requires to fetch data from a remote server from time to time.

as this "update" should be carried out even when the actual frontend app is not running, i implemented a remote service that is started on system boot. now i need to schedule a timer to start the update.

is the "Timer"-class the right one for this job? and if "yes": what is the difference between a "normal" Timer() and one started as a "daemon" by Timer(true)?

http://developer.android.com/reference/java/util/Timer.html isn't very helpful with this :(

EDIT:

ok - i see there are much more methods to do this than i expected. to clarify:

  • i want to execute some code at a time that is specified.
  • this timer is used to trigger the execution of code 7 days in the future. (i.e., every week at a given weekday and time)
  • the code should run WITHOUT waking the phone up if it is "sleeping" (screen dimmed).
  • when running the code, no activity should be started. i.e. no app pops up on the screen.
  • the code that is executed should fetch some data from the internet. if at this time no internet connection is available, the timer should be set to sth like 30 minutes and then try again.
  • after completing the code execution, the timer will be set for the next interval which will be 7 days later.
  • the timer should be started at system boot, e.g., if i reboot the phone, the timer should determine the next date to execute the code and schedule the timer. this has to work without ANY user interaction!
  • when "sleeping", the thread/service/timer/whatsoever should not consume any system resources if possible...

  • what i need is pretty much a simple unix cronjob.

i think anyone here knows "newsrob" for android? what i want to realize is pretty much the same as the newsrob-updateservice.

+3  A: 

Use AlarmManager. This allows you to set your schedule, then exit your components. Your code does not need to remain in memory and will be triggered when the alarm sounds.

i implemented a remote service that is started on system boot

Please don't do that just for a scheduled task. Use AlarmManager.

If you want the work to be done while the phone is asleep, you will need to use a _WAKEUP alarm type and perhaps use something like my WakefulIntentService to keep the device awake while the work is being done.

CommonsWare
yea, but i dont want the device to wake up when the timer fires
xenonite
@xenonite: Then don't use a `_WAKEUP` alarm, but still use `AlarmManager`.
CommonsWare
+1  A: 

I recently had to develop an application following the same pattern.

Here is how I designed it:

I created a service started either explicitely by the frontend when enabling it through a configuration dialog, either started by a BroadcastReceiver waiting for the activation of network connectivity:

    <receiver android:name=".notifications.MyReceiver">
        <intent-filter>
            <action android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"/>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        </intent-filter>
    </receiver>

The service, when started, starts a new HandlerThread, and associates it with a Looper:

public class MyService extends Service {

    private Looper serviceLooper;
    private MyHandler serviceHandler;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        //Toast.makeText(this, "service started", Toast.LENGTH_SHORT).show();

        HandlerThread thread = new HandlerThread("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        serviceLooper = thread.getLooper();
        serviceHandler = new MyHandler(this, serviceLooper);

        // initial message
        serviceHandler.sendMessage(Message.obtain());
    }

    @Override
    public void onDestroy() {
        serviceLooper.quit();
        //Toast.makeText(this, "service stopped", Toast.LENGTH_SHORT).show();
    }

}

When the network goes down or if the frontend disables it, the service is stopped, as well as the looper.

Now, in the MyHandler, I actually get the updates from the server when receiving messages.

public class MyHandler extends Handler {

    private final Context context;

    public MyHandler(Context context, Looper looper) {
        super(looper);
        this.context = context;
    }

    @Override
    public void handleMessage(Message msg) {
        // handle message and perform update
        // ...
        // try again 30 minutes
        this.sendMessageDelayed(Message.obtain(), 1000 * 60 * 30);
    }
}

The trick as you can see, is to send itself a delayed message to be handled 30 minutes later.

The advantage of this solution over using the AlarmManager is that the phone will NOT be forcibly woken up at a designed time, meaning it plays nicer with the phone resources if not needed.

Moreover, I don't start the service at boot time, only when there's an active internet connexion, and I stop it as soon as the connexion is gone.

It's been pretty efficient so far.

SirDarius
@SirDarius: I would never recommend this approach to anyone. First, task killers will nuke you from orbit. Second, Android will kill you off, once you stick around too long. Third, you are taking up a hunk of RAM that could be put to better purposes than waiting for time to elapse (e.g., supporting other activities). Fourth, your code is "forcibly woken up at a designated time", so your approach offers no advantage. Everlasting services are bad for the user and are bad for the Android ecosystem. http://www.androidguys.com/2009/09/09/diamonds-are-forever-services-are-not/
CommonsWare
You're right for most cases, and i've even considered that approach, except that i had to keep in mind for my app that the periodical "update" code would eventually have to be modified to support push notifications via a peristent TCP connection to a custom server.
SirDarius