Office Hours — Yesterday, December 7

Tuesday, December 6 | Today, December 8

Dec 7
7:25 PM
Mark M.
has entered the room
Mark M.
turned on guest access
Steve S.
has entered the room
Mark M.
hello, Steve!
Steve S.
Hi Mark!
Mark M.
how can I help you today?
Steve S.
I have a follow-up question from yesterday:
View paste
I currently have a member variable List<Contact> in a retained fragment that I use to populate a ListView. (This approach is based on a chat we had several months ago.) 

I had originally thought that once I had implemented ContactsCache, I could dispense with the member variable, and instead call ContactsCache.getContacts() to populate the ListView. But now I think I probably shouldn't do that since there is a chance that ContactsCache.getContacts() would have to retrieve the contacts from the database rather than an in-memory static variable.

If that's right, then it seems like I shouldn't call ContactsCache.getContacts() from the UI thread at all. This is disappointing, since it complicates the architecture - but maybe it's something I just have to live with. 

Am I right that I shouldn't call ContactsCache.getContacts() from the UI thread?
I'll also paste in ContactsCache to refresh your memory:
View paste
public class ContactsCache {
    static private List<Contact> sContacts == null;

    static public List<Contact> getContacts(Context c) {
        if (sContacts == null)
            sContacts = ContactDao.getContacts(c);
        return sContacts;
    }

    static public void updateContacts(Context c) {
        sContacts = ContactDao.getContacts(c);
    }
}
Mark M.
"I probably shouldn't do that since there is a chance that ContactsCache.getContacts() would have to retrieve the contacts from the database rather than an in-memory static variable" -- well, if you already are showing data in the list, that data will already be in the cache
Steve S.
ok
Mark M.
you might still want your List<Contact>, just because that represents the view on the data that the user is working with
(e.g., filtered search results)
Steve S.
ok
but at present it will be exactly the same as what's in the cache.
Mark M.
not necessarily, depending on how you are dealing with asynchronous operations (e.g., network I/O)
7:30 PM
Mark M.
however, you are correct that you need to be able to deal with cache misses
Steve S.
ok
Mark M.
you already have some logic for loading your data asynchronously (I hope)
that just turns into stuff to populate your cache
Steve S.
ok
i manage the data from an IntentService. It both communicates with the back-end and adds and deletes items from the database.
that's also where i'm at present updating the cache.
and calling ContactsCache.getContacts()
my plan at present is to use EventBus to send updated lists of contacts to the fragment and have the fragment update the ListView
Mark M.
so, pass the List<Contact> in the event
Steve S.
yes
Mark M.
and after a configuration change, call peekContacts() or something on your cache, to either get the List<Contact> or null
and if it is null, scratch your head, wonder where the contact data went, shrug your shoulders, and tell your IntentService to get busy
7:35 PM
Mark M.
and 99.999% of the time, peekContacts() will return the List<Contact> without issue
Steve S.
i'm using a retained fragment to preserve List<Contact> across configuration changes
Mark M.
since it was just a configuration change, and in your current cache setup, the data simply can't get lost
yes, but I thought your question was whether you should still do that
you can, if you want
Steve S.
yes, you're right
so it would be reasonable to handle occasional (but rare) cache misses on the UI thread?
Mark M.
no
because that will freeze the UI
Steve S.
ok, that was my concern
Mark M.
however, your current cache is effectively an all-or-nothing affair
Steve S.
ok
Mark M.
once loaded, the only way your cache goes away is if your whole process does
now, given Android's task stuff, you can't assume that the user is always coming into your process through the launcher activity
Steve S.
ok
Mark M.
you need to be in position to request that the data be loaded, in the case that it is not available
however, ideally, that's not significantly different than what happens when the user launches the app normally
and you're already handling that case by one means or another
Steve S.
right, i'm using the IntentService for that
7:40 PM
Mark M.
so, if your retained fragment gets to onViewCreated() or whatever and realizes that the retained list is null, you need to peek into your cache to see if you have the data there
in your current architecture, the answer should be no, unless I'm forgetting some scenario
and so if your fragment doesn't have the data, and the cache doesn't have the data, you kick off the IntentService to go get the data
Steve S.
ok
Mark M.
personally, because of the need to use things like an IntentService, I'm not a huge fan of getContacts() doing the lazy load itself
the cache should be pretty much just that: a cache
Steve S.
so what you meant by peekCache() is a method that checks to see if the static member variable that holds the contacts is null?
Mark M.
or something along those lines, yes
Steve S.
ok
Mark M.
(BTW, don't forget to use AtomicReference or concurrent collections or something to deal with thread safety)
now, if you change the cache to be just a cache, and move the data-load logic elsewhere, it could be that getContacts() itself is now a non-blocking call
and you don't need another separate peekContacts() method or some such
Steve S.
ok. so i would load the data from the database from somewhere else and then have a method like ContactsCache.update() to set the static member variable?
Mark M.
yes
Steve S.
ok
Mark M.
(or setContacts(), for parallelism)
Steve S.
right
7:45 PM
Steve S.
getting back to AtomicReference/concurrent collections: if I only set the ContactsCache static member variable from the IntentService, would I still need to worry about that?
Mark M.
yes
Steve S.
ok
Mark M.
at least use AtomicReference
to basically ensure an atomic swap of List<Contact>
Steve S.
i see
Mark M.
assuming that the service is always building a complete new List<Contact> every time
Steve S.
that's what i have now
Mark M.
that's simple on the threading standpoint
Steve S.
ok
Mark M.
it generates more garbage for the garbage collector
as with everything, there are trade-offs to be made
Steve S.
sure. so more garbage collection activity vs a simpler threading approach
Mark M.
right
and, in the short term, that's probably a fine tradeoff
Steve S.
ok
7:50 PM
Steve S.
now if use something like peekCache(), I'll still need to have logic to handle updating the cache using the IntentService
Mark M.
yes
Steve S.
so i'll still need to have message passing between the activity/fragment and the intentService to handle cache misses
Mark M.
from the IntentService to the activity/fragment, yes
the activity/fragment would start the IntentService on a cache miss
Steve S.
so it's not really going to give me the simpler architecture i had naively hoped for - but it could work more smoothly
Mark M.
the more useful the app, the less likely it is that it will have a simple architecture
Steve S.
right. the first version was much clunkier and much easier to write.
ok. there's a lot of material to chew on. i'll think things over.
no more questions today - thank you so much!
7:55 PM
Steve S.
have a good rest of the day!
Mark M.
you too!
Steve S.
has left the room
8:25 PM
Mark M.
turned off guest access
8:50 PM
Mark M.
has left the room
Dec 8
8:55 AM

Tuesday, December 6 | Today, December 8

 

Office Hours

People in this transcript

  • Mark Murphy
  • Steve S