Android Apps the Right Way

24

Transcript of Android Apps the Right Way

Page 1: Android Apps the Right Way
Page 2: Android Apps the Right Way

Isn’t Android just Java?

Page 3: Android Apps the Right Way

Hint: If you know Android well, put “Android” on your resume.

Page 4: Android Apps the Right Way

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.

Page 5: Android Apps the Right Way

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.

Page 6: Android Apps the Right Way

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

Page 7: Android Apps the Right Way

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

Page 8: Android Apps the Right Way

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?”

Page 9: Android Apps the Right Way

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!

Page 10: Android Apps the Right Way

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.

Page 11: Android Apps the Right Way

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!");}

}

Page 12: Android Apps the Right Way

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!");

}}

Page 13: Android Apps the Right Way

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”

Page 14: Android Apps the Right Way

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"));

}}

Page 15: Android Apps the Right Way

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");

}}

}

Page 16: Android Apps the Right Way

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 :(

Page 17: Android Apps the Right Way

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!

}

Page 18: Android Apps the Right Way

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");

}}

}

Page 19: Android Apps the Right Way

Summary

Page 20: Android Apps the Right Way

Android is not Java

Page 21: Android Apps the Right Way

Your app is ephemeral

Use Intents, don’t try to keep it running

Page 22: Android Apps the Right Way

Don’t block the UI thread

Do work asynchronously on another thread

Page 23: Android Apps the Right Way

Check for connectivity

It can go up and down at any time

Page 24: Android Apps the Right Way

And if you know Android well, put it on your resume.