android apps the right way
Upload: new-york-city-college-of-technology-computer-systems-technology-colloquium
Post on 14-Jul-2015
488 views
TRANSCRIPT
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 wrongBut 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”, or2. 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 {
@Overridepublic 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 / Threadspackage example;
import …;
/*** Displays a cat picture to the user when the download finishes.*/public class CatPictureActivity extends Activity {@Overridepublic 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 Reliabilitypackage example;
import …;
/*** Displays a cat picture to the user when the download finishes.*/public class CatPictureActivity extends Activity {@Overridepublic 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 Waypublic class CatPictureActivity extends Activity {
@Overridepublic 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.