THAT’S MY APP
       ~
RUNNING IN YOUR
 BACKGROUND
       ~
 DRAINING YOUR
   BATTERY
WHO AM I?
    Michael Galpin
 Mobile Architect, eBay
Author, Android in Practice

       @michaelg
AGENDA


Motivation

Strategies

Death

Resurrection

Advanced
TRUE MULTI-TASKING

          Multiple applications,
          executing
          simultaneously

            Consuming m...
DATA SYNC

     Apps don’t live in a
     vacuum

       Users use other
       apps / website

       Other users interac...
LOOK MA, NO HANDS


Start your app without
the user launching it

Extend app execution
after app is closed (long
running t...
STRATEGIES
SERVICES 101

<manifest>
    <application android:icon="@drawable/icon"
         android:label="@string/app_name">
       ...
SERVICES 101
public class PortfolioManagerService extends Service {

    @Override
    public void onCreate() {
    }

   ...
POLLING
@Override
public void onCreate() {
    super.onCreate();
    final long fiveMinutes = 5*60*1000;
    final Handler...
NOTIFICATIONS
private void createHighPriceNotification(Stock stock) {
    // Get the system service
    NotificationManage...
START ME UP!
<receiver android:name="PortfolioStartupReceiver"
    android:process=":stocks_background">
        <intent-f...
DATA MANAGEMENT

        Service and app use the
        same data

        Use the Service for any
        access

      ...
ACTIVITY -> SERVICE
                COMMUNICATION
public class Stock implements Parcelable{
    // fields, getter, setters...
ACTIVITY -> SERVICE
              COMMUNICATION
 Generated
public interface IStockService extends IInterface{
    /** Loca...
ACTIVITY -> SERVICE
  COMMUNICATION
public class ViewStocks extends ListActivity {
    private IStockService stockService;...
DEATH OF A SERVICE
PSYCHOPATHIC USERS

Settings ->
Applications ->
Running
Services ->
KILL!

3rd Party “Task
Managers”

FUD (Thanks
Apple)
MANAGE SERVICES
PROCESS
PRIORITIZATION
    Foreground

      Visible


      Service

    Background
      Empty
PROCESS
PRIORITIZATION
    Foreground

      Visible


      Service

    Background
      Empty
PROCESS
PRIORITIZATION
    Foreground

      Visible


      Service

    Background
      Empty
PROCESS
PRIORITIZATION
    Foreground

      Visible


      Service

    Background
      Empty
PROCESS
PRIORITIZATION
    Foreground

      Visible


      Service

    Background
      Empty
PROCESS
PRIORITIZATION
    Foreground

      Visible


      Service

    Background
      Empty
RESURRECTION
CONCEPT: USE THE OS
HELLO
       ALARMMANAGER!

public class PortfolioStartupReceiver extends BroadcastReceiver {
    @Override
    public voi...
SLEEP IS FOR THE WEAK!
public class AlarmReceiver extends BroadcastReceiver {
    private static PowerManager.WakeLock wak...
SHARING A WAKELOCK
public class PortfolioManagerService extends Service {
    @Override
    public int onStartCommand(Inte...
STRATEGY: THE SHORT-
   LIVED SERVICE

           Start at device boot

           Use AlarmManager

             Start ea...
ADVANCED
DIY PUSH

Persistent TCP
connection

Long-lived service

  Frequent alarms

  Keep-alives, re-
  connect

Always in-sync
CLOUD TO DEVICE
MESSAGING (C2DM)

         Requires Android 2.2

           And a lot of
           permissions

         ...
C2DM
<receiver android:name=".PushReceiver"
    android:permission="com.google.android.c2dm.permission.SEND">
    <intent-...
C2DM
public class PushReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent...
STRATEGY: REMOTE
     RESURRECTION
Use C2DM

Send minimal Intents

  Start (short lived)
  Service

    Retrieve/sync data...
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
Upcoming SlideShare
Loading in …5
×

That’s My App - Running in Your Background - Draining Your Battery

5,974 views

Published on

You have seen the ads where Android based devices like to brag about how awesome their multitasking is and now even the iPhone claims to have multitasking. Unfortunately it’s pseudo-multitasking borrowed from Android, but fear not. Android has “real” multitasking as well. It’s easy to do, but even easier to screw up. In this talk you’ll learn how to do it right, and how to do it without killing a phone’s battery. We’ll discuss the dreaded “P” word (polling), as well as alternatives such as Android’s cloud to device messaging and persistent connections.

0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,974
On SlideShare
0
From Embeds
0
Number of Embeds
9
Actions
Shares
0
Downloads
108
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

That’s My App - Running in Your Background - Draining Your Battery

  1. 1. THAT’S MY APP ~ RUNNING IN YOUR BACKGROUND ~ DRAINING YOUR BATTERY
  2. 2. WHO AM I? Michael Galpin Mobile Architect, eBay Author, Android in Practice @michaelg
  3. 3. AGENDA Motivation Strategies Death Resurrection Advanced
  4. 4. TRUE MULTI-TASKING Multiple applications, executing simultaneously Consuming memory CPU cycles (battery) Using I/O But not like Desktop
  5. 5. DATA SYNC Apps don’t live in a vacuum Users use other apps / website Other users interact Keep data sync Improve user experience
  6. 6. LOOK MA, NO HANDS Start your app without the user launching it Extend app execution after app is closed (long running tasks) Interact with other apps
  7. 7. STRATEGIES
  8. 8. SERVICES 101 <manifest> <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:process=":stocks_background" android:name=".PortfolioManagerService" android:icon="@drawable/icon" android:label="@string/service_name"/> <!-- ... --> </manifest>
  9. 9. SERVICES 101 public class PortfolioManagerService extends Service { @Override public void onCreate() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { } @Override public IBinder onBind(Intent intent) { } }
  10. 10. POLLING @Override public void onCreate() { super.onCreate(); final long fiveMinutes = 5*60*1000; final Handler handler = new Handler(); handler.postDelayed(new Runnable(){ public void run(){ updateStockData(); handler.postDelayed(this, fiveMinutes); } }, fiveMinutes); }
  11. 11. NOTIFICATIONS private void createHighPriceNotification(Stock stock) { // Get the system service NotificationManager mgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Create the ticker int dollarBill = R.drawable.dollar_icon; String shortMsg = "High price alert: " + stock.getSymbol(); long time = System.currentTimeMillis(); Notification n = new Notification(dollarBill, shortMsg, time); // Create expanded info and what happens when tapped String title = stock.getName(); String msg = "Current price $" + stock.getCurrentPrice() + " is high"; Intent i = new Intent(this, NotificationDetails.class); i.putExtra("stock", stock); PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0); n.setLatestEventInfo(this, title, msg, pi); // Sound! n.defaults |= Notification.DEFAULT_SOUND; // Vibrate!! long[] steps = {0, 500, 100, 200, 100, 200}; n.vibrate = steps; // Flashing LED lights !!! n.ledARGB = 0x80009500; n.ledOnMS = 250; n.ledOffMS = 500; n.flags |= Notification.FLAG_SHOW_LIGHTS; // Post the Notification mgr.notify(HIGH_PRICE_NOTIFICATION, n); }
  12. 12. START ME UP! <receiver android:name="PortfolioStartupReceiver" android:process=":stocks_background"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> public class PortfolioStartupReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent stockService = new Intent(context, PortfolioManagerService.class); context.startService(stockService); } }
  13. 13. DATA MANAGEMENT Service and app use the same data Use the Service for any access Service can cache data Pre-emptive retrieval
  14. 14. ACTIVITY -> SERVICE COMMUNICATION public class Stock implements Parcelable{ // fields, getter, setters private Stock(Parcel parcel){ this.readFromParcel(parcel); } public static final Parcelable.Creator<Stock> CREATOR = new Parcelable.Creator<Stock>() { public Stock createFromParcel(Parcel source) { return new Stock(source); Stock.aidl } public Stock[] newArray(int size) { package com.flexware.stocks; return new Stock[size]; } parcelable Stock; }; public int describeContents() { return 0; // nothing special here! IStockService.aidl } public void writeToParcel(Parcel parcel, int flags) { package com.flexware.stocks.service; parcel.writeString(symbol); parcel.writeDouble(maxPrice); import com.flexware.stocks.Stock; parcel.writeDouble(minPrice); ... interface IStockService{ } Stock addToPortfolio(in Stock stock); public void readFromParcel(Parcel parcel){ symbol = parcel.readString(); List<Stock> getPortfolio(); maxPrice = parcel.readDouble(); } minPrice = parcel.readDouble(); ... } }
  15. 15. ACTIVITY -> SERVICE COMMUNICATION Generated public interface IStockService extends IInterface{ /** Local-side IPC implementation stub class. */ public static abstract class Stub extends Binder implements IStockService{ } } public class PortfolioManagerService extends Service { @Override public IBinder onBind(Intent intent) { return new IStockService.Stub() { public Stock addToPortfolio(Stock stock) throws RemoteException { ... } public List<Stock> getPortfolio() throws RemoteException { ... } }; } }
  16. 16. ACTIVITY -> SERVICE COMMUNICATION public class ViewStocks extends ListActivity { private IStockService stockService; private ServiceConnection connection = new ServiceConnection(){ public void onServiceConnected(ComponentName className, IBinder service) { stockService = IStockService.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName className) { stockService = null; } }; @Override public void onCreate(Bundle savedInstanceState) { ... bindService(new Intent(IStockService.class.getName()), connection, Context.BIND_AUTO_CREATE); new AsyncTask<Void,Void,List<Stock>>(){ @Override protected List<Stock> doInBackground(Void... params) { return stockService.getPortfolio(); } @Override protected void onPostExecute(List<Stock> dbStocks){ // update UI } }
  17. 17. DEATH OF A SERVICE
  18. 18. PSYCHOPATHIC USERS Settings -> Applications -> Running Services -> KILL! 3rd Party “Task Managers” FUD (Thanks Apple)
  19. 19. MANAGE SERVICES
  20. 20. PROCESS PRIORITIZATION Foreground Visible Service Background Empty
  21. 21. PROCESS PRIORITIZATION Foreground Visible Service Background Empty
  22. 22. PROCESS PRIORITIZATION Foreground Visible Service Background Empty
  23. 23. PROCESS PRIORITIZATION Foreground Visible Service Background Empty
  24. 24. PROCESS PRIORITIZATION Foreground Visible Service Background Empty
  25. 25. PROCESS PRIORITIZATION Foreground Visible Service Background Empty
  26. 26. RESURRECTION
  27. 27. CONCEPT: USE THE OS
  28. 28. HELLO ALARMMANAGER! public class PortfolioStartupReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent i = new Intent(context, AlarmReceiver.class); PendingIntent sender = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); Calendar now = Calendar.getInstance(); now.add(Calendar.MINUTE, 2); long fifteenMinutesInMillis = 15*60*1000; mgr.setRepeating(AlarmManager.RTC_WAKEUP, now.getTimeInMillis(), fifteenMinutesInMillis, sender); } }
  29. 29. SLEEP IS FOR THE WEAK! public class AlarmReceiver extends BroadcastReceiver { private static PowerManager.WakeLock wakeLock = null; private static final String LOCK_TAG = "com.flexware.stocks"; 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); Intent stockService = new Intent(context, PortfolioManagerService.class); context.startService(stockService); } }
  30. 30. SHARING A WAKELOCK public class PortfolioManagerService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { updateStockData(); return Service.START_STICKY; } private void updateStockData(){ try{ ... } finally { AlarmReceiver.releaseLock(); stopSelf(); } } }
  31. 31. STRATEGY: THE SHORT- LIVED SERVICE Start at device boot Use AlarmManager Start early and often Stop Service when work is done Fly under the radar
  32. 32. ADVANCED
  33. 33. DIY PUSH Persistent TCP connection Long-lived service Frequent alarms Keep-alives, re- connect Always in-sync
  34. 34. CLOUD TO DEVICE MESSAGING (C2DM) Requires Android 2.2 And a lot of permissions Send Intents directly to device Awkward authN/authZ
  35. 35. C2DM <receiver android:name=".PushReceiver" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name= "com.google.android.c2dm.intent.RECEIVE" /> <category android:name="com.flexware.stocks" /> </intent-filter> <intent-filter> <action android:name= "com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="com.flexware.stocks" /> </intent-filter> </receiver> ... <uses-sdk android:minSdkVersion="8" /> <uses-permission android:name="android.permission.INTERNET"/> <permission android:name="com.flexware.stocks.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="com.flexware.stocks.permission.C2D_MESSAGE"/> <uses-permission android:name= "com.google.android.c2dm.permission.RECEIVE"/> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
  36. 36. C2DM public class PushReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { AlarmReceiver.acquireLock(context); if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) { onRegistration(context, intent); } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { onMessage(context, intent); } } private void onRegistration(Context context, Intent intent) { final String regId = intent.getStringExtra("registration_id"); if (regId != null) { AccountManager mgr = AccountManager.get(context); Account googleAccount = mgr.getAccountsByType("com.google")[0]; mgr.getAuthToken(googleAccount, "ah", true, new AccountManagerCallback<Bundle>(){ public void run(AccountManagerFuture<Bundle> future) { Bundle bundle = future.getResult(); String authToken = bundle.get(AccountManager.KEY_AUTHTOKEN).toString(); sendToServer(regId,authToken); } },null); } } private void sendToServer(String regId, String authToken) { ... release lock! } private void onMessage(Context context, Intent intent){ } }
  37. 37. STRATEGY: REMOTE RESURRECTION Use C2DM Send minimal Intents Start (short lived) Service Retrieve/sync data Notifications Activity only use cache

×