44
Connecting Android Apps to REST APIs Services Jason Ostrander XDA Con - August 2013

Connecting Android Apps to REST APIs Services2013-xda-devcon.sites.xda-developers.com/.../sites/3/2013/08/REST.pdf · Connecting Android Apps to REST APIs Services Jason Ostrander

  • Upload
    lekhanh

  • View
    218

  • Download
    2

Embed Size (px)

Citation preview

Connecting Android Apps to REST APIs

ServicesJason Ostrander

XDA Con - August 2013

Challenges

● Data connections are slow and unreliable

● CPU/Memory constraints

● Aggressive power management

● Touch UI accentuates performance issues

Basics

● Two networking APIs:○ Apache HttpClient○ HttpUrlConnection■ Recommended API for >2.2

● org.json.* for parsing JSON● XML Parsing○ org.xml.sax○ org.xmlpull - Android recommended

Helpful Libraries

● Parsing○ GSON - converts JSON to POJOs

● Networking○ AsyncHttp - abstracts async network

requests, returns JSON○ Volley - from Google, supports SPDY and

image loading○ OkHttp - from Square, mirrors Apache

HTTPClient API, supports SPDY

Battery Life

● Mobile devices achieve long battery life by

sleeping as much as possible

● Reduce battery consumption by:

○ Batching network activity

○ Sync only on WiFi or when charging

○ Coordinating syncing with other apps

● Use a wake lock to keep device awake

ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo info = mgr.getActiveNetworkInfo();if (info != null && info.isConnected()) { // Good to go}

Detect active network

Detecting WiFi

NetworkInfo info = mgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

if (info != null && info.isConnected()) { // You’ve got WiFi}

Monitor changes in connectivity

● Register to receive android.net.conn.

CONNECTIVITY_CHANGE or android.net.wifi.

STATE_CHANGE

● Happens often, so disable when not needed

● BroadcastReceivers are short lived: start a

Service

Manifest example

<receiver android:name="com.example.xdacon.ConnectivityReceiver" android:enabled="true" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter></receiver>

BroadcastReceiver

public class ConnectivityReceiver extends BroadcastReceiver {

@Override public void onReceive(Context context, Intent intent) { ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = mgr.getActiveNetworkInfo(); // Check connection and start networking... } }

Detecting charge state

IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);Intent intent = context.registerReceiver(null, filter);

intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);

Caching - File Cache

● Internal cache○ File file = Context.getCacheDir();○ Limited space○ Periodically flushed by the OS

● External cache○ File file = Context.getExternalCacheDir();

● Both caches are cleared when your app is uninstalled

● OSS Library: DiskLruCache

Caching - Database

● Use SqliteOpenHelper to manage DB○ Handles creation and versioning

● Use ContentProvider as interface to DB○ Thread safe○ Supports Loaders○ Works across apps○ Required by SyncAdapter

● OSS library: ProviGen

ContentProvider

@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

// query for a database cursor // ... return cursor;}

URIs

● Access a ContentProvider using a URI

● URIs are strings representing content. Like URLs.

○ Example: content://media/audio/artists/41

● Use path to refer to all items

○ Example: content://media/audio/artists/ for all

artists

ContentResolver

Uri uri = Uri.parse("content://com.example.xdacon/my_table/3");

ContentValues values = new ContentValues();values.put("my_int", 0);

context.getContentResolver().insert(uri, values);

Optimizing UI

● The Main Thread

○ Also called the UI Thread

○ All UI drawing is done on this thread

○ Also used for callbacks into your code

○ All interaction with the UI must be done by

the main thread

● DO NOT BLOCK THE MAIN THREAD

StrictMode

public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); if (BuildConfig.DEBUG) StrictMode.enableDefaults(); }}

● Detects blocking of the Main Thread

● Can generate logs, dialogs, or crash your app

● Don’t run it in production

AsyncTask

public class MyAsyncTask extends AsyncTask<String, Integer, Void> {

@Overrideprotected Void doInBackground(String... params) {

// Long running operation...

publishProgress(progress);

return null;}

AsyncTask

@Overrideprotected void onPreExecute() { // Runs before doInBackground}

@Overrideprotected void onProgressUpdate(Integer... values) { // Runs after publishProgress}

@Overrideprotected void onPostExecute(Void result) { // Runs after doInBackground}

AsyncTask

MyAsyncTask task = new MyAsyncTask();

task.execute(string1, string2);

IntentService

● Inherits from Service

● Uses a Handler to run a background thread

● onHandleIntent - your code goes here

● Block as long as you wish

IntentService

public class ExampleIntentService extends IntentService {

public ExampleIntentService() { super("ExampleIntentService"); }

@Override protected void onHandleIntent(Intent intent) { // Runs on a background thread }}

Sending Data to Services

String url = "www.google.com";Intent intent = new Intent(this, ExampleService.class);intent.putExtra("url", url);startService(intent);

protected void onHandleIntent(Intent intent) { String url = intent.getStringExtra("url"); // connect to your web service}

Activ

itySe

rvic

e

Loaders

● Simplifies querying for data and displaying it

● Reloads when data changes

● Handles Activity lifecycle for you

● Comes in two flavors: AsyncTaskLoader and

CursorLoader

Loader Example

public class ExampleListActivity extends ListActivityimplements LoaderCallbacks<Cursor> {

CursorAdapter mAdapter;

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Create adapter...

setListAdapter(mAdapter);getLoaderManager().initLoader(0, null, this);

}

// Rest of code...

Loader Example

@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {

if (id == 0) {return new CursorLoader(this, uri, projection,

selection, selectionArgs, sortOrder);}return null;

}

@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

mAdapter.swapCursor(cursor);}

Intent

notifyChangeContentResolver

onLoadFinished

Activity

Service

Database

Loader

Syncing Data

● AlarmManager to schedule periodic sync

● Better: Use SyncAdapter to coordinate sync

with other apps

● Best: Use GCM to initiate sync only when new

data available

Power usage

time

power

Unsynchronized

time

power

Synchronized

time

power

AlarmManager

● Schedule repeating alarms

○ Repeating vs. Inexact Repeating

● RTC vs ELAPSED_REALTIME

● Use WAKEUP to wake device from sleep

AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);am.setInexactRepeating(AlarmManager.RTC_WAKEUP, t0, interval, pi);

SyncAdapter

● Synchronizes network access across apps● Optimizes power consumption based on

connection status● Includes retry and backoff logic● Can be set to run when data changes● Tutorial from Any.DO: http://udinic.wordpress.

com/2013/07/24/write-your-own-android-sync-adapter/

SyncAdapter Requirements

● ContentProvider

● AbstractAccountAuthenticator and bound

service

● AbstractThreadedSyncAdapter and bound

service

● XML files for authenticator and SyncAdapter

● Add all of above to AndroidManifest

AbstractAccountAuthenticator

public class Authenticator extends AbstractAccountAuthenticator {

public Bundle addAccount( AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {...}

public Bundle getAuthToken( AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {...}

// ...

AddAccount

public Bundle addAccount(...) { Intent intent = new Intent(mContext.get(), LoginActivity.class); intent.putExtra( AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); Bundle b = new Bundle(); b.putParcelable(AccountManager.KEY_INTENT, intent); return b;}

AccountAuthenticatorActivity

String authToken = ... // Get auth token from REST server

Bundle bundle = new Bundle();bundle.putString(AccountManager.KEY_ACCOUNT_NAME, userName);bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken);

final Account account = new Account(userName, accountType);mAccountManager.addAccountExplicitly(account, password, userData);

setAccountAuthenticatorResult(bundle);finish();

Bound Service

public class AuthenticatorService extends Service { private Authenticator mAuthenticator; @Override public void onCreate() { mAuthenticator = new Authenticator(this); } @Override public IBinder onBind(Intent intent) { return mAuthenticator.getIBinder(); }}

AbstractThreadedSyncAdapter

public class SyncAdapter extends AbstractThreadedSyncAdapter {

@Override public void onPerformSync( Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {

// run network sync

}

// ...}

Manual Sync

Account account = new Account(userName, accountType);Bundle bundle = new Bundle();bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);ContentResolver.requestSync(account, authority, bundle);

SyncAdapter Gotchas

● New permissions

● Runs in a separate process

○ Reading preferences is difficult

● Beware sync settings

● Passwords are unencrypted

Where to get Help

● Android Training: http://developer.android.

com/training/index.html

● Android Source: https://github.

com/android/platform_frameworks_base

● My Book: http://www.peachpit.

com/androiduifundamentals

● Follow me on Twitter @jasonostrander

OSS Links

● Android Async Http: http://loopj.com/android-async-http/

● Volley: https://android.googlesource.com/platform/frameworks/volley

● Google-Gson: https://code.google.com/p/google-gson/

● OkHttp: http://square.github.io/okhttp/● Provigen: https://github.

com/TimotheeJeannin/ProviGen