Android Apps the Right Way

Post on 14-Jul-2015

475 views 0 download

Tags:

Transcript of 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 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.