SlideShare a Scribd company logo
Android Apps the Right Way
Isn’t Android just Java?
≠
Hint: If you know Android well, put
“Android” on your resume.
Key Differences
• Heterogeneous device capabilities
• Limited system resources
• Background tasks periodically killed
• Apps “stack” Activities
• Events handled by the OS
• Background processing common
• Blocking in UI thread yields an ANR
• Homogeneous virtual machine
• Lots of CPU and memory
• Lots of battery power
• Apps run in a dispatch loop
• Events handled within the app
• Background processing unusual
• Blocking in UI events semi-OK.
What happens when you get it wrong?
• Weird behavior when launching app
• App doesn’t function when phone sleeps
• Battery life and network issues
• Now you can’t access locks.
Common Pitfalls: Always Foreground
Problem: Android can kill background apps at any time to free resources.
Naïve solution #1: Ignore this.
“Why did I stop receiving
{mail, notifications, cat pictures} from your app?”
“It was supposed to alert me when ____, but didn’t”
“My music stopped playing in the middle of a song”
xkcd.com/937
When you know you’re doing it wrong
But don’t want to fix it
Can’t close this, ever… “…Or you’ll die in a car crash” So it kills your battery instead
The Right Way
Android apps can be killed any time they’re in the background. Accept this.
“Two hands clap and there is a sound.
What is the sound of one hand?”
The Right Way
Android will start your app again and restore its state when…
1. “Something interesting happens”, or
2. The user launches it again from history
onRestoreInstanceState()The catch: you have to write
The catch: you have to tell it what’s interesting!
Expressing Interest With Intents
PendingIntent
Intent
Intents will start your activity up again if it’s down.
PendingIntents tell Android to send them when certain things happen.
The Bad Way
package example;
import android.app.IntentService;
import android.content.Intent;
import java.util.concurrent.TimeUnit;
/**
* Prints out a message after 5 minutes - how not to do it.
*/
public class BadService extends IntentService {
public BadService() {
super("BadService");
}
@Override protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(TimeUnit.MINUTES.toMillis(5));
} catch (InterruptedException e) {
// Because this is really bad code, we also ignore interrupts.
}
System.out.println("Your eggs are ready!");
}
}
The Right Way: Use Intents to Wake Your App
package example;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import java.util.Calendar;
/**
* Prints out a message after 5 minutes.
*/
public class GoodService extends IntentService {
public GoodService() {
super("GoodService");
}
public static void schedule(Context sender) {
AlarmManager alarmManager = (AlarmManager) sender.getSystemService(Context.ALARM_SERVICE);
Calendar alarmTime = Calendar.getInstance();
alarmTime.add(Calendar.MINUTE, 5);
Intent startGoodService = new Intent(sender, GoodService.class);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, alarmTime.getTimeInMillis(),
PendingIntent.getService(sender, 0, startGoodService, 0));
}
@Override protected void onHandleIntent(Intent intent) {
System.out.println("Your eggs are ready!");
}
}
Common Pitfalls: Work on the UI Thread
Draw! Draw!
I have to find this file first!
The UI thread is busy responding to the user. Never distract it.
Jeff Miracola, Wizards of the Coast – “Frantic Search”
The Bad Waypackage example;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import java.io.InputStream;
import java.net.URL;
/**
* Displays a cat picture to the user, but crashes with a NetworkOnMainThreadException first.
*/
public class CatPictureActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
try {
drawCatPics();
} catch (Exception e) {
Log.e(getClass().getName(), "I haz an exception :(", e);
finish();
}
}
private void drawCatPics() throws Exception {
URL catApi = new URL("http://thecatapi.com/api/images/get");
InputStream catStream = (InputStream) catApi.getContent();
findViewById(R.id.cat_pics).setImageDrawable(Drawable.createFromStream(catStream, "Cats"));
}
}
The Right Way: AsyncTasks / Threads
package example;
import …;
/**
* Displays a cat picture to the user when the download finishes.
*/
public class CatPictureActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
final ListenableFutureTask<Drawable> catDownloader = ListenableFutureTask.create(new CatCall());
catDownloader.addListener(new Runnable() {
@Override public void run() {
try {
findViewById(R.id.cat_pics).setImageDrawable(catDownloader.get());
} catch (Exception e) {
Log.e(getClass().getName(), "I haz an exception :(", e);
finish();
}
}
}, Executors.newSingleThreadExecutor());
}
private class CatCall implements Callable<Drawable> {
@Override public Drawable call() throws Exception {
URL catApi = new URL("http://thecatapi.com/api/images/get");
InputStream catStream = (InputStream) catApi.getContent();
return Drawable.createFromStream(catStream, "Cats");
}
}
}
Common Pitfalls: Assuming Network Reliability
package example;
import …;
/**
* Displays a cat picture to the user when the download finishes.
*/
public class CatPictureActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
final ListenableFutureTask<Drawable> catDownloader = ListenableFutureTask.create(new CatCall());
catDownloader.addListener(new Runnable() {
@Override public void run() {
try {
findViewById(R.id.cat_pics).setImageDrawable(catDownloader.get());
} catch (Exception e) {
Log.e(getClass().getName(), "I haz an exception :(", e);
finish();
}
}
}, Executors.newSingleThreadExecutor());
}
private class CatCall implements Callable<Drawable> {
@Override public Drawable call() throws Exception {
URL catApi = new URL("http://thecatapi.com/api/images/get");
InputStream catStream = (InputStream) catApi.getContent();
return Drawable.createFromStream(catStream, "Cats");
}
}
}
What if Wifi is down?
No cat pics :(
The Right Way: Network State Intents
android.net.ConnectivityManager.CONNECTIVITY_ACTION
Android broadcasts an intent called
When the network state changes
and then you can check
ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
// Cat pics, here we come!
}
The Right Way
public class CatPictureActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
registerReceiver(new NetReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
private void setupTask() {
final ListenableFutureTask<Drawable> catDownloader = ListenableFutureTask.create(new CatCall());
catDownloader.addListener(new Runnable() {
@Override public void run() {
try {
findViewById(R.id.cat_pics).setImageDrawable(catDownloader.get());
} catch (Exception e) {
Log.e(getClass().getName(), "I haz an exception :(", e);
finish();
}
}
}, Executors.newSingleThreadExecutor());
}
private class NetReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
unregisterReceiver(this); // Prevents a second run if the network goes down.
setupTask();
}
}
}
private class CatCall implements Callable<Drawable> {
@Override public Drawable call() throws Exception {
URL catApi = new URL("http://thecatapi.com/api/images/get");
InputStream catStream = (InputStream) catApi.getContent();
return Drawable.createFromStream(catStream, "Cats");
}
}
}
Summary
Android is not Java
≠
Your app is ephemeral
Use Intents, don’t try to keep it running
Don’t block the UI thread
Do work asynchronously on another thread
Check for connectivity
It can go up and down at any time
And if you know Android well,
put it on your resume.

More Related Content

PDF
Knock, knock, who is there? Doze.
PDF
Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera
PDF
React & The Art of Managing Complexity
KEY
Cross-platform logging and analytics
PDF
Getting physical with web bluetooth in the browser hackference
PDF
Performance #1: Memory
PPTX
Android Intermediatte IAK full
PDF
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015
Knock, knock, who is there? Doze.
Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera
React & The Art of Managing Complexity
Cross-platform logging and analytics
Getting physical with web bluetooth in the browser hackference
Performance #1: Memory
Android Intermediatte IAK full
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015

What's hot (12)

PPTX
Guide to Destroying Codebases The Demise of Clever Code
PPTX
IoT Fire Starter
PDF
Capacity Management for Web Operations
PDF
Writing Virtual And Augmented Reality Apps With Web Technology
PDF
Rethink Async With RXJS
PDF
Creating Asha Games: Game Pausing, Orientation, Sensors and Gestures
PPTX
Beacons, Raspberry Pi & Node.js
PPTX
Meet the Eclipse SmartHome powered Mars Rover
PPT
Angular js meetup
PDF
Need 4 Speed FI
PDF
Forensic Tools for In-Depth Performance Investigations
PPT
Naive application development
Guide to Destroying Codebases The Demise of Clever Code
IoT Fire Starter
Capacity Management for Web Operations
Writing Virtual And Augmented Reality Apps With Web Technology
Rethink Async With RXJS
Creating Asha Games: Game Pausing, Orientation, Sensors and Gestures
Beacons, Raspberry Pi & Node.js
Meet the Eclipse SmartHome powered Mars Rover
Angular js meetup
Need 4 Speed FI
Forensic Tools for In-Depth Performance Investigations
Naive application development
Ad

Viewers also liked (20)

PDF
Test Dependencies and the Future of Build Acceleration
PDF
PDF
Ontology-based Classification and Faceted Search Interface for APIs
PPTX
4150415
PDF
Donor voice pretest tool webinar deck_final
PDF
BloomCon 2015 - Adrian Sargeant
PDF
15 Nonprofits #CrushingIt on Social Media
PPTX
OER and MOOC initiatives in Romania. Scenarios for integrating MOOCs in scho...
PDF
When Disaster Strikes
PPTX
Blogger
PDF
file_Progress_Note_16_VC_Development_and_the_Poor[1]
PPTX
When new meets old - online research methods for governement
PPT
Process Improvement - 10 Essential Ingredients
PDF
Sentence 1
PPS
Deja Las Lagrimas Rodar
PDF
I heart presentation design
DOCX
El proceso tecnologico
PPTX
Ecim smart mobility issy 13032014
DOC
Namasmaran And Romance Dr Shriniwas Janardan Kashalikar
PDF
10 Essential Ingredients for Successful Corporate Coaching Programs
Test Dependencies and the Future of Build Acceleration
Ontology-based Classification and Faceted Search Interface for APIs
4150415
Donor voice pretest tool webinar deck_final
BloomCon 2015 - Adrian Sargeant
15 Nonprofits #CrushingIt on Social Media
OER and MOOC initiatives in Romania. Scenarios for integrating MOOCs in scho...
When Disaster Strikes
Blogger
file_Progress_Note_16_VC_Development_and_the_Poor[1]
When new meets old - online research methods for governement
Process Improvement - 10 Essential Ingredients
Sentence 1
Deja Las Lagrimas Rodar
I heart presentation design
El proceso tecnologico
Ecim smart mobility issy 13032014
Namasmaran And Romance Dr Shriniwas Janardan Kashalikar
10 Essential Ingredients for Successful Corporate Coaching Programs
Ad

Similar to Android Apps the Right Way (20)

PDF
Android life cycle
PDF
Android Jumpstart Jfokus
PPT
Android overview
PDF
Marakana android-java developers
PDF
Android101
PDF
Inside the android_application_framework
PPTX
The happy path to Android development
PDF
The Rounds Project: Growing from thousands to millions - Berry Ventura & Yoah...
PDF
Android Working in the Background
PPT
cpuk10745
PPSX
Android OS and its Features
PDF
Android Survival Guide - Two years of software development
PPTX
Rounds tips & tricks
PDF
Mobile Application Development -Lecture 09 & 10.pdf
PDF
Android Basics
PPTX
Process Management in Android
PPTX
Threads handlers and async task, widgets - day8
PPTX
Efficient Android Threading
PPT
Android_Workshop
PDF
Android programming -_pushing_the_limits
Android life cycle
Android Jumpstart Jfokus
Android overview
Marakana android-java developers
Android101
Inside the android_application_framework
The happy path to Android development
The Rounds Project: Growing from thousands to millions - Berry Ventura & Yoah...
Android Working in the Background
cpuk10745
Android OS and its Features
Android Survival Guide - Two years of software development
Rounds tips & tricks
Mobile Application Development -Lecture 09 & 10.pdf
Android Basics
Process Management in Android
Threads handlers and async task, widgets - day8
Efficient Android Threading
Android_Workshop
Android programming -_pushing_the_limits

More from New York City College of Technology Computer Systems Technology Colloquium (9)

PDF
Towards Improving Interface Modularity in Legacy Java Software Through Automa...
PDF
Data-driven, Interactive Scientific Articles in a Collaborative Environment w...
PPTX
Pharmacology Powered by Computational Analysis: Predicting Cardiotoxicity of ...
PPTX
How We Use Functional Programming to Find the Bad Guys
Towards Improving Interface Modularity in Legacy Java Software Through Automa...
Data-driven, Interactive Scientific Articles in a Collaborative Environment w...
Pharmacology Powered by Computational Analysis: Predicting Cardiotoxicity of ...
How We Use Functional Programming to Find the Bad Guys

Recently uploaded (20)

PPTX
Big Data Technologies - Introduction.pptx
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
sap open course for s4hana steps from ECC to s4
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Empathic Computing: Creating Shared Understanding
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
Spectroscopy.pptx food analysis technology
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
Big Data Technologies - Introduction.pptx
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
sap open course for s4hana steps from ECC to s4
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Unlocking AI with Model Context Protocol (MCP)
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Encapsulation_ Review paper, used for researhc scholars
Empathic Computing: Creating Shared Understanding
NewMind AI Weekly Chronicles - August'25 Week I
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Review of recent advances in non-invasive hemoglobin estimation
Digital-Transformation-Roadmap-for-Companies.pptx
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Spectroscopy.pptx food analysis technology
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
The Rise and Fall of 3GPP – Time for a Sabbatical?

Android Apps the Right Way

  • 3. Hint: If you know Android well, put “Android” on your resume.
  • 4. Key Differences • Heterogeneous device capabilities • Limited system resources • Background tasks periodically killed • Apps “stack” Activities • Events handled by the OS • Background processing common • Blocking in UI thread yields an ANR • Homogeneous virtual machine • Lots of CPU and memory • Lots of battery power • Apps run in a dispatch loop • Events handled within the app • Background processing unusual • Blocking in UI events semi-OK.
  • 5. What happens when you get it wrong? • Weird behavior when launching app • App doesn’t function when phone sleeps • Battery life and network issues • Now you can’t access locks.
  • 6. Common Pitfalls: Always Foreground Problem: Android can kill background apps at any time to free resources. Naïve solution #1: Ignore this. “Why did I stop receiving {mail, notifications, cat pictures} from your app?” “It was supposed to alert me when ____, but didn’t” “My music stopped playing in the middle of a song” xkcd.com/937
  • 7. When you know you’re doing it wrong But don’t want to fix it Can’t close this, ever… “…Or you’ll die in a car crash” So it kills your battery instead
  • 8. The Right Way Android apps can be killed any time they’re in the background. Accept this. “Two hands clap and there is a sound. What is the sound of one hand?”
  • 9. The Right Way Android will start your app again and restore its state when… 1. “Something interesting happens”, or 2. The user launches it again from history onRestoreInstanceState()The catch: you have to write The catch: you have to tell it what’s interesting!
  • 10. Expressing Interest With Intents PendingIntent Intent Intents will start your activity up again if it’s down. PendingIntents tell Android to send them when certain things happen.
  • 11. The Bad Way package example; import android.app.IntentService; import android.content.Intent; import java.util.concurrent.TimeUnit; /** * Prints out a message after 5 minutes - how not to do it. */ public class BadService extends IntentService { public BadService() { super("BadService"); } @Override protected void onHandleIntent(Intent intent) { try { Thread.sleep(TimeUnit.MINUTES.toMillis(5)); } catch (InterruptedException e) { // Because this is really bad code, we also ignore interrupts. } System.out.println("Your eggs are ready!"); } }
  • 12. The Right Way: Use Intents to Wake Your App package example; import android.app.AlarmManager; import android.app.IntentService; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import java.util.Calendar; /** * Prints out a message after 5 minutes. */ public class GoodService extends IntentService { public GoodService() { super("GoodService"); } public static void schedule(Context sender) { AlarmManager alarmManager = (AlarmManager) sender.getSystemService(Context.ALARM_SERVICE); Calendar alarmTime = Calendar.getInstance(); alarmTime.add(Calendar.MINUTE, 5); Intent startGoodService = new Intent(sender, GoodService.class); alarmManager.setExact(AlarmManager.RTC_WAKEUP, alarmTime.getTimeInMillis(), PendingIntent.getService(sender, 0, startGoodService, 0)); } @Override protected void onHandleIntent(Intent intent) { System.out.println("Your eggs are ready!"); } }
  • 13. Common Pitfalls: Work on the UI Thread Draw! Draw! I have to find this file first! The UI thread is busy responding to the user. Never distract it. Jeff Miracola, Wizards of the Coast – “Frantic Search”
  • 14. The Bad Waypackage example; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.Log; import java.io.InputStream; import java.net.URL; /** * Displays a cat picture to the user, but crashes with a NetworkOnMainThreadException first. */ public class CatPictureActivity extends Activity { @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); try { drawCatPics(); } catch (Exception e) { Log.e(getClass().getName(), "I haz an exception :(", e); finish(); } } private void drawCatPics() throws Exception { URL catApi = new URL("http://thecatapi.com/api/images/get"); InputStream catStream = (InputStream) catApi.getContent(); findViewById(R.id.cat_pics).setImageDrawable(Drawable.createFromStream(catStream, "Cats")); } }
  • 15. The Right Way: AsyncTasks / Threads package example; import …; /** * Displays a cat picture to the user when the download finishes. */ public class CatPictureActivity extends Activity { @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); final ListenableFutureTask<Drawable> catDownloader = ListenableFutureTask.create(new CatCall()); catDownloader.addListener(new Runnable() { @Override public void run() { try { findViewById(R.id.cat_pics).setImageDrawable(catDownloader.get()); } catch (Exception e) { Log.e(getClass().getName(), "I haz an exception :(", e); finish(); } } }, Executors.newSingleThreadExecutor()); } private class CatCall implements Callable<Drawable> { @Override public Drawable call() throws Exception { URL catApi = new URL("http://thecatapi.com/api/images/get"); InputStream catStream = (InputStream) catApi.getContent(); return Drawable.createFromStream(catStream, "Cats"); } } }
  • 16. Common Pitfalls: Assuming Network Reliability package example; import …; /** * Displays a cat picture to the user when the download finishes. */ public class CatPictureActivity extends Activity { @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); final ListenableFutureTask<Drawable> catDownloader = ListenableFutureTask.create(new CatCall()); catDownloader.addListener(new Runnable() { @Override public void run() { try { findViewById(R.id.cat_pics).setImageDrawable(catDownloader.get()); } catch (Exception e) { Log.e(getClass().getName(), "I haz an exception :(", e); finish(); } } }, Executors.newSingleThreadExecutor()); } private class CatCall implements Callable<Drawable> { @Override public Drawable call() throws Exception { URL catApi = new URL("http://thecatapi.com/api/images/get"); InputStream catStream = (InputStream) catApi.getContent(); return Drawable.createFromStream(catStream, "Cats"); } } } What if Wifi is down? No cat pics :(
  • 17. The Right Way: Network State Intents android.net.ConnectivityManager.CONNECTIVITY_ACTION Android broadcasts an intent called When the network state changes and then you can check ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = cm.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { // Cat pics, here we come! }
  • 18. The Right Way public class CatPictureActivity extends Activity { @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); registerReceiver(new NetReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); } private void setupTask() { final ListenableFutureTask<Drawable> catDownloader = ListenableFutureTask.create(new CatCall()); catDownloader.addListener(new Runnable() { @Override public void run() { try { findViewById(R.id.cat_pics).setImageDrawable(catDownloader.get()); } catch (Exception e) { Log.e(getClass().getName(), "I haz an exception :(", e); finish(); } } }, Executors.newSingleThreadExecutor()); } private class NetReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if (info != null && info.isConnected()) { unregisterReceiver(this); // Prevents a second run if the network goes down. setupTask(); } } } private class CatCall implements Callable<Drawable> { @Override public Drawable call() throws Exception { URL catApi = new URL("http://thecatapi.com/api/images/get"); InputStream catStream = (InputStream) catApi.getContent(); return Drawable.createFromStream(catStream, "Cats"); } } }
  • 20. Android is not Java ≠
  • 21. Your app is ephemeral Use Intents, don’t try to keep it running
  • 22. Don’t block the UI thread Do work asynchronously on another thread
  • 23. Check for connectivity It can go up and down at any time
  • 24. And if you know Android well, put it on your resume.