Android long running service with alarm manager an

2019-07-08 08:45发布

问题:

I have a Service that uses a custom Connection class (extends thread) to a hardware controller. When the User prefers, I wish to maintain this connection on a permanent basis. I already have the code to handle when the Android device loses its internet connection, switches between wi-fi, etc.

In order to stay connected, the controller requires that you speak to it within every 5 minutes. I currently, within the Connection class start a thread that runs in a while(), and checks the system time and the last time it communicated, and when > 4 minutes it requests a status. For some reason, at different times the communication doesn't occur in time. i.e., occurs after 5 minutes. The Service doesn't die, as far as I can tell but the "Ping" to the controller is late. This doesn't happen when I have the phone plugged into the charger (or debugger). Additionally, the behavior is the same when I move the Service to the foreground.

Does the phone slow down it's processor when it goes to sleep?

Is there a better way?

I'm thinking it's the AlarmManger, but I'm having trouble getting it to work with an inner-class, within the Service. I tried using the API demos as a starting point, but I can't seem to figure out how to get the Broadcast receiver registered. I am trying to register the receiver programmatically, with no changes to the manifest.

public class DeviceConnectionService extends Service {

    @Override
    public void onCreate() {
        Intent intent = new Intent(this, PingConnection.class);
        intent.setAction("KEEP_CONNECTION_ALIVE");
        PendingIntent sender = PendingIntent.getBroadcast(this,
            0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        // We want the alarm to go off 30 seconds from now.
        long firstTime = SystemClock.elapsedRealtime();
        firstTime += 15*1000;
        // Schedule the alarm!
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                firstTime, 15*1000, sender);
        // register to listen to the Alarm Manager
        if (mPingConnectionReceiver == null) {
            mPingConnectionReceiver = new PingConnection();
            getApplicationContext().registerReceiver(mPingConnectionReceiver,
             new IntentFilter("KEEP_CONNECTION_ALIVE"));
        }
    }

    // ...

    public class PingConnection extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (dBug) Log.i("PingConnection", "Pinging Controller");
            // do real work here
        }
    }
}

回答1:

Does the phone slow down it's processor when it goes to sleep?

The phone shuts down its processor when it goes to sleep. That is the definition of "sleep".

I'm thinking it's the AlarmManger, but I'm having trouble getting it to work with an inner-class, within the Service. I tried using the API demos as a starting point, but I can't seem to figure out how to get the Broadcast receiver registered. I am trying to register the receiver programatically, with no changes to the manifest.

That is an unusual approach for AlarmManager. That being said, since you declined to describe "having trouble" in any detail, it is difficult to help you.

Get rid of getApplicationContext() (you don't need it and really don't want it in this case). I would register the receiver before touching AlarmManager. Before you go to production, please choose an action name that has your package name in it (e.g., com.something.myapp.KEEP_CONNECTION_ALIVE).

Beyond that, check LogCat for warnings.


UPDATE

In your LogCat, you should have a warning from AlarmManager complaining about not being able to talk to your BroadcastReceiver.

Replace:

Intent intent = new Intent(this, PingConnection.class);
intent.setAction("KEEP_CONNECTION_ALIVE");

with:

Intent intent = new Intent("KEEP_CONNECTION_ALIVE");

and you may have better luck.



回答2:

you can't register AlarmManager in a Service. All you can do is declare it as global in the Manifest.xml. You can start the alarm from service in this way, by declaring it in Manifest.xml

If you have a remote service and you close the launcher activity, the AlarmManager will still run, but don't forget to stop it on onDestroy() method of the service.

I've tried to register only in the Service the AlarmManager as I didn't used it for the main activity, but no success! It didn't work as registering as a normal BroadCastReceiver.

that's how the things are, you have to declare it in Manifest.xml as global



回答3:

I know it's late, but maybe it's useful for someone else.

You can register it, the problem is when the Intent tries to call it.

Instead of calling it like this:

Intent intent = new Intent(this, PingConnection.class);

Create an empty intent and add an action you are going to listen to:

Intent intent = new Intent();
intent.setAction("value you want to register");

Then create the pending intent and send the broadcast like you have it.

Create an attribute for the receiver so you can access it in the whole class and unregister if necessary (if the pendingIntent is also an attribute you can unregister any time):

private PingConnection pingConnection = new PingConnection();

Register it like this:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("the value you used before");
getApplicationContext().registerReceiver(pingConnection, filter);

Now you won't get any errors, and the class is not static, and it's an inner class.