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 displayModel
from different perspectives. Currently I have aListActivity
for all elements, and anActivity
for one element's details - Sometimes
Model
needs refreshing. Surely this is done on a different thread. Refreshing can be triggered from severalActivity
'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 inService#onDestroyed()
, so I want to minimize that by binding it toActivity
's. At this point, I'm also not sure how to deliver the updated information: whether to provide a getter inBinder
, or include that in the broadcast message. - Customize the
Application
object so that refreshing methods and getters are available globally. I then perform update fromActivity
's usingAsyncTask
. If there are otherActivity
's that are behind the currentActivity
, they will update inonResume()
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
开发者_C百科 }
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.
Service
s 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
.
Well, you could extend a BroadcastReceiver instead of a Service, and when it's done doing what it needs to do, it loads up an Activity with the results.
You didn't explain what kind of info are you getting but this line is important:
These tasks involve states that need to be loaded and saved gracefully when the application starts and stops
If that is the case, why don't you do an AsynTask
inside the Activity
?
I had your same worries about sending Intents
with ArrayList
inside but I have an app which does exactly that and I am not having performance issues.
精彩评论