Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC in Android: Application or Service for asynchronous updates?

(I'm sorry for not being so clear in my first post)

Here is the situation: I have data that is to be refreshed from the Internet. Let's call it Model.

What I want to do: Basically it sounds like an MVC model, where the Model is also kept persistent in local (private) storage. The Model and its associated methods are application-wise. There are several Activity's that display and manipulate different aspects of it:

  • User navigates across different Activity's that display Model from different perspectives. Currently I have a ListActivity for all elements, and an Activity for one element's details
  • Sometimes Model needs refreshing. Surely this is done on a different thread. Refreshing can be triggered from several Activity's.
  • There are several (time consuming) common tasks that can be triggered from different Activity's
  • My application loads and saves Model to private storage when it starts and stops

My problem: I'm not sure where to put Model and the related tasks in. Also, I don't know what mechanism to use to notify Activity's. Currently I come up with 2 approaches:

  • Use Service and send broadcasts. Saving to disk is performed in Service#onDestroyed(), so I want to minimize that by binding it to Activity's. At this point, I'm also not sure how to deliver the updated information: whether to provide a getter in Binder, or include that in the broadcast message.
  • Customize the Application object so that refreshing methods and getters are available globally. I then perform update from Activity's using AsyncTask. If there are other Activity's that are behind the current Activity, they will update in onResume() when the user navigates back.

Reasons I'm not using a class with static methods:

  • I need to save and store Model to disk.
  • Some of the methods need a Context for displaying toasts, notifications, caching, etc.

Also, I don't put these functionalities in an Activity because there are several activities that manipulate the same piece of persistent data.

Below are pseudocode illustrating what I mean:

Using Service:

/** Service maintaining state and performing background tasks */
class MyService extends Service {
    Model mModel;
    Binder mBinder;

    onCreate() {
        super.onCreate();
        mBinder = new Binder();
        // load mModel from disk, or do default initialization
    }

    onDestroy() {
        super.onDestroy();
        // save mModel to disk
    }

    onBind() {
        return mBinder;
    }

    class Binder {
        refresh() {
            new AsyncTask() {
                doInBackground() {
                    // update mModel from Internet
                }
                onPostExecute() {
                    sendBroadcasts(new Intent("my.package.REFRESHED"));
                }
            }.execute();
        }

        getState() {
            return mModel.getState();
        }
    }
}

/** Activity displaying result */
class MyActivity extends ListActivity {
    MyService.Binder mBinder;

    onCreate() {
        super.onCreate();
        // register mReceiver
        // bind service
    }

    onDestroy() {
        super.onDestroy();
        // unbind service
        // unregister mReceiver
    }

    /** Invokes time-consuming update */
    refresh() {
        // binding is asynchronous, and user may trigger refreshing too early
        if (mBinder != null) {
            mBinder.refresh();
        }
    }

    BroadcastReceiver mReceiver = new BroadcastReceiver() {
        onReceive(Intent intent) {
            if ("my.package.REFRESHED".equals(intent.getAction())
                    && mBinder != null) {
                updateViews(mBinder.getState());
            }
        }
    };
}

Make the functionality globally accessible in the custom Application object

/** Custom Application providing domain specific functionalities */
class MyApplication extends Application {
    Model mModel;

    onCreate() {
        super.onCreate();
        // load mModel from disk, or do default initialization
    }

    onTerminate() {
        super.onTerminate();
        // save mModel to disk
    }

    void refresh() {
        /** time-consuming */
    }

    getState() {
        return mModel.getState();
    }
}

/** Activity displaying result */
class MyActivity extends ListActivity {
    onResume() {
        super.onResume();

        // in case some top Activities have refreshed
        // and user is navigating back
        updateViews(((MyApplication)getApplicationContext()).getState());
    }

    /** Invokes time-consuming update */
    refresh() {
        new AsyncTask() {
            doInBackground() {
                ((MyApplication)getApplicationContext()).refresh();
            }
            onPostExecute() {
                // update the ListView according to result
                updateViews(((MyApplication)getApplicationContext()).getState());
            }
        }.execute();
    }
}

Weaknesses I can think of for the Service approach is complexity, since Binding is asynchronous. And it's very likely that I have to repeat some code because I have both ListActivity and Activity

For the Application approach, the documentation says not to rely on onTerminate() being called.

I know I'm being very awkward. What is the conventional way to solve this sort of problem?

Many thanks.

like image 855
Phil Avatar asked Jul 03 '10 10:07

Phil


1 Answers

Services are mostly suitable for something that is not bound to a single Activity (and usually work together with NotificationManager or a Widget). This doesn't seem to be the case.
So my suggestion is to have a well-engineered AsyncTask that manages state via SharedPreferences/SQLite itself (instead of abusing Applicaion) and will be launched from the ListActivity.

like image 75
yanchenko Avatar answered Oct 05 '22 12:10

yanchenko