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
- Aerogear www.aerogear.org
- AGReddit github.com/secondsun/AGReddit
- Examples github.com/secondsun/devnexus2013demo
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