Skip to content
svenmeier edited this page May 27, 2012 · 35 revisions

Propoid-util offers convenience working with services and content.

Asynchronous tasks and their observers

TaskService offers an easy management of asynchronous tasks and their observers. Instead of nesting an AsyncTask in your activity, creation and management of tasks is located in a service for the following benefits:

  • being independent of an activity all tasks survive orientation changes
  • a task scheduled by one activity can publish its data to another subscribed activity
  • tasks can be executed in serial or in parallel
  • tasks can be prioritized, merged or subsumed
  • the result of a task can be cached in the service
  • a task scheduled by network or sensor events can publish its data to any subscribed activity
    public class FooService extends TaskService<FooObserver> {

      // must be public
      public class BarTask extends Task {
        private int current;
        private int max;

        // must be public, argument is optional
        public BarTask(Intent intent) {
          max = intent.getIntExtra("max", 10);
        }

        // asynchronously executed
        protected void onExecute() {
          while (current < max) {
            expensiveCalculation();

            current++;

            // initiate publish
            publish();
          }
        }

        // publish on main thread
        protected void onPublish(FooObserver observer) {
           observer.onBar(current);
        }
      }
    }

To let your activity observe tasks you just have to create a subclass of TaskObserver (it's recommended to put this class besides your service class):

    public class FooObserver extends TaskObserver {
      public void onBar(int current) {}
    }

Your activity can subscribe an observer in onStart():

    FooObserver observer = new FooObserver() {
      // called on main thread
      public void onBar(int current) {
        mProgress.setProgress(current);
      } 
    };

    public void onStart() {
      super.onStart();

      observer.subscribe(context, FooService.class);
    }

A task can be started similar to starting a service. The utility method createIntent(Context, Class<? extends Task>) creates a suitable intent to let TaskService instantiate the correct task instance:

    Intent intent = FooService.createIntent(this, BarTask.class)
    intent.putExtra("max", 10);
    startService(intent);

Don't forget to unsubscribe the observer in onStop():

    public void onStop() {
      observer.unsubscribe()

      super.onStop();
    }

By default all Tasks are executed in parallel. But you can easily let your tasks decide how to handle other tasks to be scheduled:

    private class BarTask extends Task {
      public boolean onScheduling(Task other) {
        if (other instanceof BarTask) {
           // skip redundant task
           return true;
        } else if (other instanceof BazTask) {
          // must be delayed until this task has finished
          delay(other);
          return true;
        }

        // unrelated, let it be scheduled in parallel
        return false; 
      }
    }

Preferences

Preference allows convenient typed access to a preference. Note how to apply a fallback value and a minimal safeguard:

    Preference<Boolean> myBoolean = Preference.getBoolean(context, R.string.my_boolean).fallback(true);
    Preference<Integer> myInteger = Preference.getInt(context, R.string.my_integer).minimum(2);

    if (myBoolean.get()) {
      myInteger.set(4);
    }

Preference keys are defined as string resources (we recommend to use a separate file res/values/keys.xml), thus ensuring compile-time safe identification of each preference:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string name="my_boolean">my_boolean</string>
   <string name="my_integer">my_integer</string>
   <string name="my_list">my_list</string>
</resources>

You can store and retrieve a list of values too:

    Preference<Integer> myList = Preference.getInt(context, R.string.my_list);

    myList.setList(Arrays.asList(1, 2, 3, 4));
    
    for (int i : myList.getList()) {
      ...
    }

Most of the time (but not necessarily) you want the user to be able to change the preference:

    <CheckBoxPreference
        android:key="@string/my_boolean"
        android:title="@string/my_boolean_title"
        android:summary="@string/my_boolean_summary"
        android:defaultValue="true"
    />
    <EditTextPreference
        android:key="@string/my_integer"
        android:title="@string/my_integer_title"
        android:summary="@string/my_integer_summary"
        android:numeric="integer"
        android:defaultValue="10"
    />

Note that in the latter case - an EditTextPreference with numeric input - any entered String will automatically be converted to the requested type when accessed via Preference.

Clone this wiki locally