Architecting Android Apps

Posted on May 8, 2012 (one year ago). Seen 4,291 times. 2 comments. Permalink Feed
Photo Marko Gargenta
@MarkoGargenta
Member since Jan 19, 2007
Location: San Francisco
Stream Posts: 42
Tagged as: Android Tutorial

Architecting Android Apps

Marko Gargenta @Marakana

1. About Marko Gargenta

images/Marko-Promo.png
Figure 1. Marko Gargenta

1.1. Entrepreneur, Author, Speaker

  • Developer of Android Bootcamp for Marakana.

  • Instructor for 1,000s of developers on Android at Qualcomm, Cisco, Motorola, Intel, DoD and other great orgs.

  • Author of Learning Android published by O???Reilly.

  • Speaker at OSCON (4x), ACM, IEEE(2x), SDC(2x), AnDevCon(2x), DroidCon.

  • Co-Founder of SFAndroid.org

  • Co-Chair of Android Open conference: Android Open

2. Overview

Seven iterations of an app:

  • Part 1 - Activities and Android UI

  • Part 2 - Intents, Action Bar, and More

  • Part 3 - Services

  • Part 4 - Content Providers

  • Part 5 - Lists and Adapters

  • Part 6 - Broadcast Receivers

  • Part 7 - App Widgets

3. Yamba

images/Yamba-Splash.png
Figure 2. Yamba

3.1. Objectives of Yamba

The objective of this module is to explain how to go about designing a typical Android app. You will have a chance to see an example app, Yamba, using most of the standard Android building blocks. By the end of this talk, you should have high-level understanding about designing an Android app.

4. Yamba Approach

Yamba Overview
  • Yamba: Yet Another Micro-Blogging App

  • Comprehensive Android example application.

  • Works with services that support Twitter API.

Project Philosophy
  • Small increments - build it organically

  • App must always run - whole and complete

  • Refactor when needed - do simplest thing first

5. Part 1 - Activities and Android UI

images/Yamba/Yamba-1.svg
Figure 3. Yamba Part 1

6. Activity Overview

images/Activities.png
Figure 4. Example of Activities
  • An activity is roughly a screen.

  • A typical application may have many activities.

  • Activities are typically expensive and so are managed by the system.

7. Activity Lifecycle

images/ActivityLifecycle.png
Figure 5. Activity Lifecycle
  • Activities are highly managed by the system’s ActivityManager.

  • ActivityManager drives the activity through its states.

  • You as an app developer get to say what happens on transitions.

8. Activity Lifecycle Explored

Starting for the first time
D/ActivityDemo( 6708): onCreate
D/ActivityDemo( 6708): onStart
D/ActivityDemo( 6708): onResume
Opening another activity, then clicking Back button
...
D/ActivityDemo( 6708): onClickAnotherActivity
D/ActivityDemo( 6708): onPause
D/ActivityDemo( 6708): onStop
D/ActivityDemo( 6708): onRestart
D/ActivityDemo( 6708): onStart
D/ActivityDemo( 6708): onResume
Rotating the screen
...
D/ActivityDemo( 6708): onPause
D/ActivityDemo( 6708): onStop
D/ActivityDemo( 6708): onDestroy
D/ActivityDemo( 6708): onCreate
D/ActivityDemo( 6708): onStart
D/ActivityDemo( 6708): onResume
Pressing Home button
...
D/ActivityDemo( 6708): onPause
D/ActivityDemo( 6708): onStop
Restarting form the StatusBar
...
D/ActivityDemo( 6708): onRestart
D/ActivityDemo( 6708): onStart
D/ActivityDemo( 6708): onResume

9. Activity Template

ActivityDemo.java
package com.marakana.android.lifecycle;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class ActivityDemo extends Activity {
  static final String TAG = "ActivityDemo";

  // --- Lifecycle methods

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Log.d(TAG, "onCreate");
  }

  @Override
  protected void onStart() {
    super.onStart();
    Log.d(TAG, "onStart");
  }

  @Override
  protected void onResume() {
    super.onResume();
    Log.d(TAG, "onResume");
  }

  @Override
  protected void onPause() {
    super.onPause();
    Log.d(TAG, "onPause");
  }

  @Override
  protected void onStop() {
    super.onStop();
    Log.d(TAG, "onStop");
  }

  @Override
  protected void onRestart() {
    super.onRestart();
    Log.d(TAG, "onRestart");
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy");
  }

  // --- Options menu methods

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.item_start_service:
      startService(new Intent(this, ServiceDemo.class));
      return true;
    case R.id.item_stop_service:
      stopService(new Intent(this, ServiceDemo.class));
      return true;
    case R.id.item_refresh:
      startService(new Intent("marakana.intent.action.IntentServiceDemo"));
      return true;
    case R.id.item_send_broadcast:
      sendBroadcast(new Intent("marakana.intent.action.ReceiverDemo"));
      return true;
    case R.id.item_location:
      startActivity(new Intent(this, SystemServicesDemo.class));
      return true;
    case R.id.item_insert:
      getContentResolver()
          .insert(ProviderDemo.CONTENT_URI, new ContentValues());
      return true;
    case R.id.item_query:
      getContentResolver().query(ProviderDemo.CONTENT_URI, null, null, null,
          null);
      return true;
    }

    return false;
  }

  // --- Button click event handler

  public void onClickAnotherActivity(View v) {
    startActivity(new Intent(this, AnotherActivity.class));
    Log.d(TAG, "onClickAnotherActivity");
  }

}

10. Activity Callbacks

10.1. Lifecycle

onCreate()

Used to setup your activity. You will almost always have to have it. Good place to inflate the UI and setup listeners.

onResume() and onPause()

Use them to turn on and off things that you’d like to have running only while the activity is visible. This is important for things that consume a lot of battery, such as GPS and sensors.

onStart() and onStop()

Use to setup code that starts/stops the activity. Unlike onResume() and onPause(), it includes Paused state as well.

onRestart()

Called when the activity is restarted. It is followed by onStart() and onResume().

onDistroy()

A good place to do any cleanup before the activity is cleaned up from memory. This is the counter-part to onCreate().

10.2. Other

onCreateOptionsMenu() and onOptionsItemSelected()

Use them to setup your menu. onOptionsItemSelected() loads the menu, typically from an XML resource. onOptionsItemSelected() is called whenever an option menu item is clicked on.

Various listeners and event handlers, such as onClick()

Used to handle the UI events.

11. Registering Activity

11.1. Main Activity

Register the activity in the Android Manifest file:

Android Manifest file
...
<activity
  android:name=".ActivityDemo"
  android:label="@string/app_name">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
...

The intent filter specifies that this activity is to be the main entry point into the application as well as that it should be shown in the app launcher on home screen.

11.2. Any other activity

Android Manifest file
...
<activity android:name=".AnotherActivity"></activity>
...

12. Building Android UI

There are two approaches to building Android UI:

12.1. Declaratively

  • Declare UI in XML

  • Eclipse provides nice drag-n-drop tools

  • Inflate the XML views in Java

12.2. Programmatically

  • Instantiate all widgets programmatically

  • Set properties for each

The best is to use both:

  1. Star with declaring the look and feel using XML

  2. Inflate XML into Java

  3. Finish by programming the actions using Java

13. Layouts and Views

images/LayoutsAndViews.png
Figure 6. Layouts and Views

14. Part 2 - Intents, Action Bar, and More

images/Yamba/Yamba-2.svg
Figure 7. Yamba Part 2

15. Intent Overview

images/Intents.png
Figure 8. Example of Intents
  • Intents are like events or messages.

  • You can use them so start activities, start/stop services, or send broadcasts.

  • Intents can be implicit or explicit.

16. Using Intents

startActivity()

Starts an activity specified by the intent. If activity does not exist already, it calls onCreate() to create it. Otherwise, it calls onStart() and onResum().

startService()

Starts a service. Even if the service is not created yet, it called onCreate() on the service first.

stopService()

Stops a service that is already running. If service is not running, it does nothing.

bindService()

Binds to a service. Requires that the service returns a binder via onBind() method.

sendBroadcast()

Sends a broadcast. If there’s a broadcast receiver registered to filter for the same action as this intent is specifying, that receiver’s onReceive() method will be called.

17. Explicit and Implicit Intents

17.1. Explicit Intent

ActivityDemo.java
...
startActivity(new Intent(this, AnotherActivity.class));
...
startService(new Intent(this, ServiceDemo.class));
...

this is the context from which this intent is being sent, in our case an Activity.

17.2. Implicit Intent

ActivityDemo.java
...
startService(new Intent("marakana.intent.action.IntentServiceDemo"));
...
sendBroadcast(new Intent("marakana.intent.action.ReceiverDemo"));
...

Requires that there’s an intent filter filtering for this particular intent, for example:

AndroidManifest.xml
...
<service android:name=".IntentServiceDemo">
  <intent-filter>
    <action android:name="marakana.intent.action.IntentServiceDemo" />
  </intent-filter>
</service>
...
<receiver android:name=".ReceiverDemo">
  <intent-filter>
    <action android:name="marakana.intent.action.ReceiverDemo" />
  </intent-filter>
</receiver>
...

18. Intent Filter

Intent filter is a way for us to assign certain action to an activity, service, receiver or similar.

Action is one of system defined actions, or something you come up with.

Intent filter typically goes into Android Manifest file, within <activity>, <service>, or <receiver> elements.

Android Manifest file
  ...
  <intent-filter>
    <action android:name="some.action.goes.here" />
  </intent-filter>
  ...

19. Action Bar

The action bar, introduced in Honeycomb (API 11) is a title bar that includes:

  • The application icon

  • The activity title

  • A set of user-selectable actions (optional)

  • A set of user-selectable navigation modes (optional)

Action Bar

20. Enabling the Action Bar

Android automatically displays an action bar on an API 11+ system if the <uses-sdk> element of your applications manifest:

  • Sets minSdkVersion to 11 or later, or

  • Sets targetSdkVersion to 11 or later

Either or these settings enable the "holographic look and feel" introduced in Honeycomb, which includes action bar support.

  • If neither minSdkVersion nor targetSdkVersion are set to 11+, then an API 11+ system renders the app in a legacy theme, without action bar support.

With the action bar enabled, legacy option menu items appear automatically in the action bar’s overflow menu. You reveal the overflow menu with:

  • The hardware Menu button (if present), or

  • An additional button in the action bar (for devices without a hardware Menu button)

21. Adding Action Items

To display an option menu item as an action item, in the menu resource file add android:showAsAction="ifRoom" to the <item> element.

  • The device will display the item if there is room available in the action bar, otherwise the item appears in the overflow menu.

  • Devices running API 10 or earlier ignore the showAsAction attribute.

If your menu item supplies both a title and an icon, the action item shows only the icon by default.

  • To display the text title, add withText to the android:showAsAction attribute. For example:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/ic_menu_save"
          android:title="@string/menu_save"
          android:showAsAction="ifRoom|withText" />
</menu>
Note The withText value is a hint to the action bar. The action bar will show the title if possible, but might not if an icon is available and the action bar is constrained for space.

22. ICS Split Action Bars

API 14 introduced split action bar support.

  • Enabling split action bar allows a device with a narrow screen to display a separate action bar at the bottom of the screen containing all action items.

You enable split action bar in the manifest add uiOptions="splitActionBarWhenNarrow":

  • To an <activity> element to enable split action bar for that activity, or

  • To the <application> element to enable split action bar for all activities

Note Devices on API 13 or earlier ignore the uiOptions attribute.

23. Using the App Icon for Navigation

You can enable the application icon — which appears in the action bar on the left side — to behave as an action item. If enabled, your app should respond by:

  • Going to the application "home" activity, or

  • Navigating "up" the application’s structural hierarchy

When the user taps the app icon, the system calls your activity’s onOptionsItemSelected() method. The MenuItem passed as an argument has an ID of android.R.id.home.

Note

The app icon was enabled by default in APIs 11-13. API 14 disables it be default. To enable it in API 14 or later:

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        getActionBar().setHomeButtonEnabled(true);
}

24. Part 3 - Services

images/Yamba/Yamba-3.svg
Figure 9. Yamba Part 3

25. Service Overview

images/Service.svg
Figure 10. Example of a Service
  • Services are code that runs in the background.

  • They can be started and stopped. Services doesn???t have UI.

  • Keep in mind that service runs on the main application thread, the UI thread.

26. Service Lifecycle

images/ServiceLifecycle.png
Figure 11. Service Lifecycle
  • Service starts and "runs" until it gets a request to stop.

  • Service will run on the main UI thread.

  • To offload work from main thread, use intent service.

  • Intent service uses worker thread, stops when done with work.

  • Services can be bound or unbound.

27. Service Lifecycle Explored

Starting a service first time
D/ServiceDemo( 7623): onCreate
D/ServiceDemo( 7623): onStartCommand
Restarting the same service
...
D/ServiceDemo( 7623): onStartCommand
Stopping the service
...
D/ServiceDemo( 7623): onDestroy
Starting the intent service
D/IntentServiceDemo( 7748): onCreate
D/IntentServiceDemo( 7748): onHandleIntent for action: marakana.intent.action.IntentServiceDemo
D/IntentServiceDemo( 7748): onDestroy

28. Service Template

ServiceDemo.java
package com.marakana.android.lifecycle;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class ServiceDemo extends Service {
  static final String TAG = "ServiceDemo";

  @Override
  public IBinder onBind(Intent arg0) {
    return null;
  }

  @Override
  public void onCreate() {
    Log.d(TAG, "onCreate");
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "onStartCommand");
    return  START_STICKY;
  }

  @Override
  public void onDestroy() {
    Log.d(TAG, "onDestroy");
  }

}

29. IntentService Template

IntentServiceDemo.java
package com.marakana.android.lifecycle;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

public class IntentServiceDemo extends IntentService {
  static final String TAG = "IntentServiceDemo";

  public IntentServiceDemo() {
    super(TAG);
  }

  @Override
  public void onCreate() {
    super.onCreate();
    Log.d(TAG, "onCreate");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    Log.d(TAG, "onHandleIntent for action: " + intent.getAction());
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy");
  }

}

30. Service Callbacks

onBind()

Required, but for unbound services, we just return null.

onCreate()

Called when service is first created.

onStartCommand()

Called is called every time service is started.

onDestroy()

Called when service is stopped. It is subsequently destroyed.

31. IntentService Callbacks

Constructor

It needs to pass the name of this service to its super.

onCreate()

Called when service is first created.

onHandleIntent()

This is where the work of the service runs.

onDestroy()

Called when service is stopped. It is subsequently destroyed.

32. Registering Service

Registering a service that will be called explicitly by its class name
...
<service android:name=".ServiceDemo"></service>
...
Registering a service that will be called via action
...
<service android:name=".IntentServiceDemo">
  <intent-filter>
    <action android:name="marakana.intent.action.IntentServiceDemo" />
  </intent-filter>
</service>
...

33. Part 4 - Content Providers

images/Yamba/Yamba-4.svg
Figure 12. Yamba Part 4

34. Content Provider Overview

images/ContentProvider.png
Figure 13. Example of Content Provider
  • Content Providers share content with applications across application boundaries.

  • Examples of built-in Content Providers are:

    • Contacts

    • MediaStore

    • Settings and more.

35. Typical Usage of Content Providers

images/ContentProviderUsage.svg
Figure 14. Typical Usage of Content Providers

36. Content Provider Lifecycle

images/ContentProviderLifecycle.png
Figure 15. Content Provider Lifecycle
  • Content provider is initiated first time it is used via a call to onCreate().

  • There is no callback for cleaning up after the provider.

  • When modifying the data (insert/update/delete), open/close database atomically.

  • When reading the data, leave database open or else the data will get garbage collected.

37. Content Provider Template

ProviderDemo.java
package com.marakana.android.lifecycle;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import android.provider.Settings.System;

public class ProviderDemo extends ContentProvider {
  static final String TAG = "ProviderDemo";

  static final String AUTHORITY = "content://com.marakana.android.lifecycle.providerdemo";
  public static final Uri CONTENT_URI = Uri.parse(AUTHORITY);
  static final String SINGLE_RECORD_MIME_TYPE = "vnd.android.cursor.item/vnd.marakana.android.lifecycle.status";
  static final String MULTIPLE_RECORDS_MIME_TYPE = "vnd.android.cursor.dir/vnd.marakana.android.lifecycle.status";

  @Override
  public boolean onCreate() {
    Log.d(TAG, "onCreate");
    return true;
  }

  @Override
  public String getType(Uri uri) {
    String ret = getContext().getContentResolver().getType(System.CONTENT_URI);
    Log.d(TAG, "getType returning: " + ret);
    return ret;
  }

  @Override
  public Uri insert(Uri uri, ContentValues values) {
    Log.d(TAG, "insert uri: " + uri.toString());
    return null;
  }

  @Override
  public int update(Uri uri, ContentValues values, String selection,
      String[] selectionArgs) {
    Log.d(TAG, "update uri: " + uri.toString());
    return 0;
  }

  @Override
  public int delete(Uri uri, String selection, String[] selectionArgs) {
    Log.d(TAG, "delete uri: " + uri.toString());
    return 0;
  }

  @Override
  public Cursor query(Uri uri, String[] projection, String selection,
      String[] selectionArgs, String sortOrder) {
    Log.d(TAG, "query with uri: " + uri.toString());
    return null;
  }

}

38. Content Provider Callbacks

onCreate()

Used to initialize this content provider. This method runs on UI thread, so should be quick. Good place to instantiate database helper, if using database.

getType()

Returns the mime time for the given uri. Typically, this MIME type will either be something like vnd.android.cursor.item/vnd.marakana.android.lifecycle.status for a single item or vnd.android.cursor.dir/vnd.marakana.android.lifecycle.status for multiple items.

insert()

Inserts the values into the provider returning uri that points to the newly inserted record.

update()

Updates records(s) specified by either the uri or selection/selectionArgs combo. Returns number of records affected.

delete()

Deletes records(s) specified by either the uri or selection/selectionArgs combo. Returns number of records affected.

query()

Queries the provider for the record(s) specified by either uri or`selection`/selectionArgs/grouping/having combo.

39. Registering Content Provider

Registering in Android Manifest file
...
<provider
     android:name=".ProviderDemo"
     android:authorities="com.marakana.android.lifecycle.providerdemo" />
...

The authority of this provider must match the uri authority that this provider is responding to.

40. Part 5 - Lists and Adapters

images/Yamba/Yamba-5.svg
Figure 16. Yamba Part 5

41. Lists and Adapters Overview

images/Adapter.png
Figure 17. Lists and Adapters Overview

Adapters connect potentially large data sets to small views

42. Fragments

images/Fragments.svg
Figure 18. Fragments

43. So, What’s a Fragment?

A fragment is a class implementing a portion of an activity.

  • A fragment represents a particular operation or interface running within a larger activity.

  • Fragments enable more modular activity design, making it easier to adapt an application to different screen orientations and multiple screen sizes.

  • Fragments must be embedded in activities; they cannot run independent of activities.

  • Most fragments define their own layout of views that live within the activity’s view hierarchy.

    • However, a fragment can implement a behavior that has no user interface component.

  • A fragment has its own lifecycle, closely related to the lifecycle of its host activity.

  • A fragment can be a static part of an activity, instantiated automatically during the activity’s creation.

  • Or, you can create, add, and remove fragments dynamically in an activity at run-time.

44. Loaders

Loaders make it easy to load data asynchronously in an activity or fragment. Loaders have these characteristics:

  • They are available to every Activity and Fragment.

  • They provide asynchronous loading of data.

  • They monitor the source of their data and deliver new results when the content changes.

  • They automatically reconnect to the last loader’s cursor when being recreated after a configuration change. Thus, they don’t need to re-query their data.

Loaders were introduced in Honeycomb (API 11).

  • The Android Support Package includes support for loaders. By including the support package in your application, you can use loaders even if your application for a minSdkVersion of 4 or later.

45. Using Loaders in an Application

An application that uses loaders typically includes the following:

  • An Activity or Fragment.

  • An instance of the LoaderManager.

  • A CursorLoader to load data backed by a ContentProvider. Alternatively, you can implement your own subclass of Loader or AsyncTaskLoader to load data from some other source.

  • A data source, such as a ContentProvider, when using a CursorLoader.

  • An implementation for LoaderManager.LoaderCallbacks. This is where you create new loader instances and manage your references to existing loaders.

  • A way of displaying the loader’s data, such as a SimpleCursorAdapter.

46. Availability of Fragments and Loaders

46.1. Fragments: Implemented in Honeycomb (3.0) or Later

Fragments were added to the Android API in Honeycomb, API 11.

The primary classes related to fragments are:

android.app.Fragment

The base class for all fragment definitions

android.app.FragmentManager

The class for interacting with fragment objects inside an activity

android.app.FragmentTransaction

The class for performing an atomic set of fragment operations

46.2. Fragments: Implemented in Donut (1.6) or Later

Google provides the Compatibility Package, a Java library that you can include in an application, implementing support for fragments and other Honeycomb features (loaders).

47. Part 6 - Broadcast Receivers

images/Yamba/Yamba-6.svg
Figure 19. Yamba Part 6

48. Broadcast Receiver Overview

images/BroadcastReceiver.png
Figure 20. Broadcast Receiver Lifecycle
  • An Intent-based publish-subscribe mechanism.

  • Great for listening system events such as SMS messages.

49. Broadcast Receiver Template

ReceiverDemo.java
package com.marakana.android.lifecycle;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class ReceiverDemo extends BroadcastReceiver {
  static final String TAG = "ReceiverDemo";

  @Override
  public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onReceive action: "+intent.getAction() );
  }

}

50. Receiver Lifecycle Explained

Sending a broadcast to a receiver
D/ReceiverDemo( 7964): onReceive action: marakana.intent.action.ReceiverDemo

51. Broadcast Receiver Callbacks

onReceive()

This is the only method you typically care about for a broadcast receiver. It is called when this receiver is invoked.

52. Registering Broadcast Receiver

Registering in Android Manifest file
<receiver android:name=".ReceiverDemo">
  <intent-filter>
    <action android:name="marakana.intent.action.ReceiverDemo" />
  </intent-filter>
</receiver>
Registering programmatically
...
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ...
  // Create the receiver
  receiver = new TimelineReceiver();
  filter = new IntentFilter( UpdaterService.NEW_STATUS_INTENT );
}

protected void onResume() {
  super.onResume();
  super.registerReceiver(receiver, filter,
      "com.marakana.yamba.SEND_TIMELINE_NOTIFICATIONS", null);
}

@Override
protected void onPause() {
  super.onPause();
  unregisterReceiver(receiver);
}
...

53. Part 7 - App Widgets

images/Yamba/Yamba-7.svg
Figure 21. Yamba Part 7

54. App Widgets Overview

  • App widgets are miniature views that can live in other apps, such as Home app.

  • They are a special implementation of Broadcast Receivers.

54.1. Declaring an App Widget

Widgets are essentially Broadcast Receivers

Yamba App Widget registration in AndroidManifest.xml
<receiver
    android:name=".YambaWidget"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="com.marakana.broadcast.NEW_STATUS" />
    </intent-filter>

    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/widget" />
</receiver>

54.2. Specifying Meta Data

Meta data specifies the default size of the widget, plus the update period.

Yamba Widget meta data
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minHeight="72dp"
    android:minWidth="294dp"
    android:updatePeriodMillis="10000" >
</appwidget-provider>

55. Implementing App Widget

package com.marakana.android.yamba;

...

public class YambaWidget extends AppWidgetProvider {
        private static final String TAG = YambaWidget.class.getSimpleName();

        @Override
        public void onReceive(Context context, Intent intent) {
                super.onReceive(context, intent);
                if (intent.getAction().equals(YambaApp.NEW_STATUS_BROADCAST)) {
                        AppWidgetManager appWidgetManager = AppWidgetManager
                                        .getInstance(context);
                        this.onUpdate(context, appWidgetManager, appWidgetManager
                                        .getAppWidgetIds(new ComponentName(context,
                                                        YambaWidget.class)));
                        Log.d(TAG, "onReceived");
                }
        }

        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                        int[] appWidgetIds) {
                super.onUpdate(context, appWidgetManager, appWidgetIds);

                // Get the data
                Cursor cursor = context.getContentResolver().query(
                                StatusProvider.CONTENT_URI, null, null, null,
                                StatusData.SORT_BY);

                // Do we have any data?
                if (cursor!=null && cursor.moveToFirst()) {
                        String user = cursor.getString(cursor
                                        .getColumnIndex(StatusData.C_USER));
                        String text = cursor.getString(cursor
                                        .getColumnIndex(StatusData.C_TEXT));

                        // Loop thru all the widget instances
                        for (int appWidgetId : appWidgetIds) {
                                RemoteViews views = new RemoteViews(context.getPackageName(),
                                                R.layout.row);
                                views.setTextViewText(R.id.text_user, user);
                                views.setTextViewText(R.id.text_text, text);
                                views.setTextViewText(R.id.text_createdAt, "");
                                appWidgetManager.updateAppWidget(appWidgetId, views);
                        }
                }
                Log.d(TAG, "onUpdated");
        }

}

56. Yamba App Widget Output

screens/YambaAppWidget.png
Figure 22. Yamba App Widget Output

57. Architecting Android Apps Summary

Thank you!

Marko Gargenta & Marakana Team

Special thanks to Ken Jones as well as the rest of Marakana team for research related to Fragments, Loaders, and many other new features of ICS.

Slides & video of this presentation is available at Marakana.com

Yamba source code is available at https://github.com/marakana/yamba

(c) Marakana.com

Comments

Posted on May 9, 2012
Photo Aleksandar Gargenta
Technical Program Manager
Twitter Inc
Member since Jan 19, 2007
Location: San Francisco
Awesome!
Posted on Jul 24, 2012
Photo Mfawa Alfred Onen
System Administrator
Bingham University
Member since Jul 24, 2012
Location: Abuja
Nice presentation!