restful webservices on android


About

Hoyt Summers Pittman
Sr. Software Engineer @ RedHat
www.aerogear.org

Most important point


Use Loaders if you don't care if your request completes

Use Services for long running tasks, or tasks you care about

Don't kill the users battery

Stay off the UI thread

Resources

REST in BRIEF

  •  HTTP verbs (GET, PUT, POST, DELETE)
  • URI Access (http://server.com/$resource)
  • Often JSON or XML plain text result

Android in brief

  • Open Source
  • Apps written in Java
  • Rather rigid Activity lifecycle

Consuming Services

  • Download Data
  • Parse Data
  • Store Data in Database

CONSUMING SERVICES (Linux)

curl http://devnexus.com/s/speakers.json | json speakerList.speaker
     | mongoimport --type json --jsonArray -c speakers

CONSUMING SERVICES (JAVA)


    

package net.saga.devnexus.devnexusdemo;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Hello world!
 *
 */
public class App {
    private static final Charset UTF_8 = Charset.forName("UTF-8");
    
    public static void main( String[] args ) throws MalformedURLException, IOException, JSONException {
        URL devNexusURL = null;
        HttpURLConnection devNexusConnection = null;
        InputStream devNexusInputStream = null;
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);
        byte[] byteArray = new byte[1024];
        int length = -1;
        
        devNexusURL = new URL("http://devnexus.com/s/speakers.json");

        devNexusConnection = (HttpURLConnection) devNexusURL.openConnection();
        devNexusInputStream = devNexusConnection.getInputStream();
        while((length = devNexusInputStream.read(byteArray)) != -1) {
            byteStream.write(byteArray, 0, length);
        }
        
        String httpResult = new String(byteStream.toByteArray(), UTF_8);
        
        JSONObject jsonResult = new JSONObject(httpResult);
        JSONArray speakers = jsonResult.getJSONObject("speakerList").getJSONArray("speaker");
        
        Mongo mongoClient = new Mongo();
        DB db = mongoClient.getDB( "test" );
        DBCollection speakerCollection = db.getCollection("speaker");
        for (int index = 0; index < speakers.length(); index ++ ) {
            JSONObject speaker = speakers.getJSONObject(index);
            BasicDBObject speakerDoc = new BasicDBObject();
            
            Iterator keys = speaker.keys();
            while (keys.hasNext()) {
                String key = keys.next();
                speakerDoc.append(key, speaker.getString(key));
            }
            
            speakerCollection.insert(speakerDoc);
            
        }
        
        assert speakerCollection.getCount() == speakers.length();

        
    }
    
    
}


CONSUMING SERVICES (JAVA)

Java you're drunk; GO HOME!

CONSUMING SERVICES (ANDROID)

  • Android is Linux, why won't the first one work?
  • Android is Java why won't the second one work
  • Wait a second, is this whole talk on a single slide?

Android < 3.0

  • This is a huge pain in the ass
  • Developer is responsible for managing lifecycle
  • Lots of boilerplate, very error prone
  • Many examples online still promote this pattern

It went something like this

  • User event starts a long operation
  • Activity creates a handler to the operation
  • Activity registers itself as a listener
  • Activity waits for a result
  • Activity serializes listener onPause
  • Activity deserializes listener onResume
  • Activity probes listener to figure out if it has data
  • or if it is running
  • or if it failed
  • or if it needs to be required
  • ...
  • and you often messed up one of these steps

ANDROID 4.0 and the UI thread

  • Network Access on Main Thread 
  • Don't block, ever
  • ApplicationNotResponding
  • Use Loaders

new thread(new runnable())

  • BAD JAVA DEVELOPER!
  • Android has much better tools to use.
  • Will actually work just fine if you aren't doing UI stuff
  • Run Example 1

new ASYNCTask()

  • Works better
  • onPostExecute runs on UI thread
  • Simple, but naive 
  • Run Example 2
  • Crash Example 2

implements LoaderCallbacks<t>

  • Loaders introduced in 3.0
  • Backported to 1.6
  • Solves some of the issues with AsyncTask
  • Noteblur Demo

extends IntentService

  • Can create a Service class
  • Independent of UI lifecycle
  • Can be called from other apps
  • Requires more work for simple cases
  • Can return data using ResultReceiver
  • Run Example3

Permissions

Don't forget to add the uses-permission for Internet

Authentication and authorization

  • Basically passing tokens
  • Query parameters
  • Headers
  • Request Body

oauth 1

  • Signpost is a library which works in Android
  • Use intents to catch out of band authentication
  • Noteblur Demo

oauth 2

  • Spec is somewhat of a mess
  • Usually safe using libraries provided by service

Push services on Android

  • Google Cloud Messaging
  • Requires server support
  • Requires Client Support

Cloud Messaging on Android


        
public class GCMIntentService extends GCMBaseIntentService {

    public GCMIntentService() {
		super("APP_ID");
	}

	@Override
	protected void onError(Context context, String error) {
		Log.e("GCM", error);
	}

	@Override
	protected void onMessage(Context context, Intent message) {
        //handle message
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void onRegistered(Context context, String regId) {
        ServerUtilities.register(context, regId);
	}

	@Override
	protected void onUnregistered(Context context, String regId) {
		if (GCMRegistrar.isRegisteredOnServer(context)) {
            ServerUtilities.unregister(context, regId);
        }
	}

}

    

CLOUD MESSAGING ON ANDROID


<permission
 android:name="net.saga.android.coffeeexample.permission.C2D_MESSAGE"
 android:protectionLevel="signature" />

<uses-permission 
  android:name="net.saga.android.coffeeexample.permission.C2D_MESSAGE" />
    <!-- App receives GCM messages. -->
<uses-permission 
    android:name="com.google.android.c2dm.permission.RECEIVE" />
    <!-- GCM connects to Google Services. -->
<uses-permission 
    android:name="android.permission.INTERNET" />
    <!-- GCM requires a Google account. -->
<uses-permission 
    android:name="android.permission.GET_ACCOUNTS" />
    <!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission 
     android:name="android.permission.WAKE_LOCK" />

<application
 android:allowBackup="true"
 android:icon="@drawable/ic_launcher"
 android:label="@string/app_name"
 android:theme="@style/AppTheme" >
  <receiver
    android:name="com.google.android.gcm.GCMBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
     <intent-filter>
        <action 
          android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action 
          android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category 
          android:name="net.saga.android.coffeeexample" />
      </intent-filter>
   </receiver>
   <service android:name=".GCMIntentService" />

Cloud Messaging on the SERVER


            Sender sender = new Sender("SERVER_KEY");
            Message message = new Message.Builder().build();
            MulticastResult result = sender.send(message, regIds, 5);
        

Another demo!

  • CoffeeDemo

CONTENT PROVIDERS

  • Abstractions around data in your application
  • Usually backed by SQLite
  • Exportable
  • Observable
  • Android Contact Picker is an example

aerogear

  • Cross platform mobile support libraries
  • Authentication
  • Authorization
  • Paging
  • REST
  • Offline
  • Async
  • etc

Aerogear

  • Currently in development
  • Shooting for a CR1 in March
  • Final by April

AGReddit

  • Aerogear can work without its server
  • Attempts to be back end agnostic

Power consumption

  • Radio lifecycle
  • High power when running data
  • Goes to low power
  • Goes off
  • Takes about 30 seconds to cycle

USE YOUR Tools

  • DDMS has a network viewer
  • Carriers have tools for their radios

Be thrifty

  • Cache common data
  • Batch actions up
  • Use push instead of polling

Conclusion

Questions?

restful webservices on android

By secondsun

restful webservices on android

  • 3,895