Updating a view using a handler & cleaning up the mess
I've got a class that extends a LinearLayout. My class is used in many places throughout my app. Inside my class I create a Handler:
Handler updateHandler = new Handler() {
public void handleMessage(Message msg) {
// Update image, other items, etc.
}
};
This handler is passed into a static method and saved so that whenever certain conditions arise, all the views associated with this handle can be updated accordingly.
The problem is that I believe I am holding onto the reference to the view indefinitely. So let's say for example I use my custom class in a ExtendedList and the user collapses the group that displayed a view of my class. Technically, it's no longer in use, yet it's handler will still get fired anytime the condition comes up.
Is there a better way I could be doing this? Is there a way to know when a view is being destroy/disposed and is no longer in use by the OS?
BTW: I'm targeting SDK version 7.
EDIT
To better clarify, I'll provide a real-world scenario that would be a good fit with what I'm trying to do. This is a contrive开发者_Python百科d example.
Imagine you've got a map of a country. On the map you have an ImageView for each city showing the city's current weather. When the weather changes, the ImageView needs to change to reflect the new weather condition - be it sunny, raining, snowy, etc.
To apply this to my original question, I would have created a class called WeatherView
which extended ImageView
. Inside WeatherView
I'd have a Handler which is added to some static list somewhere. In a background thread and being triggered by a timer, I consume a web service which provides the weather in the given cities. When the weather changes, I'd go to my static handler list and find out which cities need to update their image and then trigger that "event" to take place.
Now let's say the user switches states or countries, and the original cities are no longer valid. Their handler still exists in the list, so they will continue to take up memory and resources.
First of all, you should not hold references to contexts/activities/views in static fields, to avoid memory leaks. (see Avoiding Memory leaks)
I don't know what you want to do exactly, but it should be possible to create one Handler in each Activity and destroy the Handler when the Activity is destroyed. To do that use the Activity lifecycle methods.
If I understand correctly you are updating all views in your whole application (in all activities). Thats most probably not good in terms of performance.
EDIT
Views should only display data. They should never implement business logic, like fetching data.
To solve your example you can store all WeatherViews in a HashTable/ArrayList or whatever, and your Activity polls the weather service regularly. If the weather changed find the view use the HashTable to get the correct WeatherView and update it.
public class WeatherMapActivity extends ListActivity {
class WeatherData{};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ArrayAdapter<WeatherData>(this,android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<WeatherData>()));
}
class WeatherTask extends AsyncTask<String, Void, HashMap<String, WeatherData>> {
@Override
protected HashMap doInBackground(String... cities) {
HashMap<String, WeatherData> data = new HashMap<String, WeatherMapActivity.WeatherData>();
for (String city : cities) {
WeatherData fetchedData = null;
// fetch weather from server
data.put(city, fetchedData);
}
return data;
}
protected void onPostExecute(HashMap<String, WeatherData> fetchedData) {
ArrayAdapter<WeatherData> adapter = (ArrayAdapter<WeatherData>) getListAdapter();
// now remove/add/update data in the adapter
//update the list view
adapter.notifyDataSetChanged();
}
}
}
This handler is passed into a static method and saved so that whenever certain conditions arise, all the views associated with this handle can be updated accordingly.
Ick.
Is there a better way I could be doing this?
In Android, your user interface is comprised of an activity, holding onto views (and ignoring fragments for the moment). When "certain conditions arise", whatever is causing those "certain conditions" to "arise" (Service
, BroadcastReceiver
, act of $DEITY
, whatever) should notify the activity of that event. The activity, in turn, can notify whichever views need to know about that event.
You do not need a Handler
per view for this. Just have the activity call a method on your custom View
. This is lighter weight, less coding, and easier to maintain.
You do not need a static data member for this. The activity already has its views, though you may wish to maintain a (non-static) cache inside the activity for easy access to those views that need this event. This avoids any chance of memory leaks.
UPDATED (based on edited question)
Imagine you've got a map of a country. On the map you have an ImageView for each city showing the city's current weather. When the weather changes, the ImageView needs to change to reflect the new weather condition - be it sunny, raining, snowy, etc.
None of that needs a set of Handlers
, nor a static data member. It is all inside one activity.
In a background thread and being triggered by a timer, I consume a web service which provides the weather in the given cities. When the weather changes, I'd go to my static handler list and find out which cities need to update their image and then trigger that "event" to take place.
If the "background thread" is simply forked by the activity, you do not need a static data member, nor a bunch of Handlers
. You need a single Handler
, created/owned by the activity, passed into the thread (and also passed between activity instances on configuration changes), and have the thread post a single message to the Handler. The Handler and activity collaborate to notify all of the ImageViews
.
If the "background thread" is an IntentService
, the same model holds, except that you would pass a Messenger
from the activity to the service, since you cannot (and should not) pass a Handler
between components via an Intent
extra. Once again, you do not need a static data member, nor one Handler
per view.
精彩评论