android in practice_Keeping Services awake(portfolio project)

AlarmManager help us to resurrect our killed Service. But that resurrection could be short-lived.the Service needs to do—retrieve fresh stock data from the Internet and send out Notifications if needed.We want it to keep the device awake long enough to create Notifications for the user.

Android’s PowerManager API,Acquiring a WakeLock allows your application to prevent the OS from putting the device to sleep (turning off the CPU).you must list it as a <uses-permission> in your AndroidManifest.xml file.

the PARTIAL_WAKE_LOCK. This turns on the CPU, but keeps the screen turned off.

We can add code to our Service to acquire a WakeLock during its onStartCommand method, and then release it after we finish checking for Notifications. But there’s a big problem with that approach. If the device is asleep, then the WakeLock acquired by the AlarmManager will be released once the onReceive method of our AlarmReceiver class finishes. This can (and will) happen before the onStartCommand of our Service is invoked. The device could go back to sleep before we even get a chance to acquire a WakeLock. Therefore, we must acquire a WakeLock in the onReceive method of AlarmReceiver, since that’s the only place we’re guaranteed that execution won’t be suspended.

Modified AlarmReceiver, now with power management:

/**
 * A {BraodcastReceiver} is notified when a system alarm fires. It then starts the 
 * {PortfolioManagerService} passing it a {WakeLock}.
 */
public class AlarmReceiver extends BroadcastReceiver {
	private static PowerManager.WakeLock wakeLock=null;
	private static final String LOCK_TAG = "com.manning.aip.portfolio";
	public static synchronized void acquireLock(Context ctx){
		if(wakeLock==null){
			PowerManager mgr=(PowerManager)ctx.getSystemService(Context.POWER_SERVICE);
			wakeLock=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOCK_TAG);
			wakeLock.setReferenceCounted(true);
		}
		wakeLock.acquire();
	}
   public static synchronized void releaseLock(){
	   if(wakeLock!=null){
		   wakeLock.release();
	   }
   }
	@Override
	public void onReceive(Context context, Intent intent) {
		acquireLock(context);
		// TODO Auto-generated method stub
		Intent stockService=new Intent(context,PortfolioStartupReceiver.class);
		context.startService(stockService);
	}
}

Normally, to share with a Service that you’re starting, you’d pass it as part of the Intent (typically as an extra), but anything passed as part of the Intent must be a
Parcelable. A WakeLock is a representation of a system setting, it’s definitely not a Parcelable. So we use static variables and static methods to work around this.

We used a static WakeLock with static acquire/release methods so that this can be shared between the AlarmReceiver instance and our background Service.

Keep in mind that for this technique to work, AlarmReceiver and our Service must be running in the same process, or you’ll face a tricky bug.

Here’s the declaration of AlarmReceiver from our AndroidManifest.xml file:

 <receiver android:name=".AlarmReceiver" android:process=":stocks_background" />
 <uses-permission android:name="android.permission.WAKE_LOCK"/>
Now we need to add code to PortfolioManagerService to release the WakeLock so that the device can go back to sleep.

Releasing the WakeLock after checking for alerts:

private void checkForAlerts(Iterable<Stock> stocks){
		try{
			for(Stock stock:stocks){
				double current=stock.getCurrentPrice();
				if(current>stock.getMaxPrice()){
					createHighPriceNotification(stock);
					continue;
				}
				if(current<stock.getMinPrice()){
					createLowPriceNotification(stock);
				}
			}
		}finally{
			AlarmReceiver.releaseLock();
			this.stopSelf();
		}
	}



你可能感兴趣的:(android in practice_Keeping Services awake(portfolio project))