Apr 21 | 7:20 PM |
Mark M. | has entered the room |
Apr 21 | 7:25 PM |
Mark M. | turned on guest access |
Apr 21 | 7:30 PM |
Michaelidle | has entered the room |
Mark M. |
hello, Michaelidle!
|
Mark M. |
how can I help you today?
|
Apr 21 | 7:35 PM |
Michaelidle |
I've been refactoring my code to try to get the best performance when loading my MainActivity. It's a fairly simple ListView that get's loaded by my custom adapter. The Adapter is backed by a List<Conversations>. I'm creating the List<> on a background thread because I want to load the Conversation objects on a background thread because my conversations are tucked away in a SQLite DB. When my list is rebuilt from the background thread I use an eventbus to let the UI know that the data is ready. This is working fine, but every now and then I get an issue: http://stackoverflow.com/questions/3132021/andr... Is it really a hard rule that I can't update the list from the background. Seems very strange to me that this causes an exception.
|
Apr 21 | 7:40 PM |
Mark M. |
"Is it really a hard rule that I can't update the list from the background" -- yes
|
Mark M. |
however, you should not get that "every now and then", unless you are updating your list along several different code paths
|
Mark M. |
what event bus implementation are you using?
|
Apr 21 | 7:45 PM |
Michaelidle |
I'm using Otto, but I have an interesting scenario going on. I have a ConvoProvider class that tries to implement the Provider pattern. It holds a member variables of List<Conversations>, but when you do ConvoProvider.getList() it internally does return Collections.unmodifiableList(internalList);
|
Michaelidle |
Finding it tough working with list in Android to make sure that I'm not hanging the UI, loading from the background, and not modifying the list from multiple threads causing a CME>
|
Mark M. |
you might want to consider switching from Otto to greenrobot's EventBus
|
Mark M. |
then, when you post the HeyWeNeedToUpdateTheListEvent, your UI can subscribe to it specifically on the main application thread
|
Michaelidle |
Well, I have a snippet of code that allows it to update on the main thread. So the event is sent from a Separate thread, and it gets consumed on the main thread, which will either create a new adapter, and set it to the ListView or call notify data set changed on the adapter.
|
Apr 21 | 7:50 PM |
Michaelidle |
So the adapter.notifyDataSetChanged and Listview.setAdapter happens on the main thread, but the actual list is always being created/modified on a background thread.
|
Mark M. |
then you should not be getting that exception
|
Mark M. |
at least for the "created" part
|
Mark M. |
you cannot modify the contents of the list directly held by the ArrayAdapter on a background thread, at least without a corresponding notifyDataSetChanged() on the main application thread
|
Michaelidle |
So the logic is sound... I should be able to create or modify a list on the background... and then when it's done to call notify data set changed.
|
Mark M. |
the catch with the modification comes in if something triggers a UI update while you are doing the modifications
|
Mark M. |
what the exception is telling you is that what the ArrayAdapter thinks is in the list, and what is actually in the list, are two different things
|
Michaelidle |
I'm guessing it has to come down to this answer in the SO question I posted "This illegalStateException arises when a ui thread is updating the view and another background thread changes the data again. That moment causes this issue."
|
Mark M. |
right
|
Mark M. |
which is why I do not modify the list held by the ArrayAdapter on a background thread
|
Michaelidle |
Yeah, I guess that makes sense, but just feels like it could get slow.
|
Mark M. |
you are welcome to do the I/O and other heavy work on the background thread to load the new list contents
|
Michaelidle |
Maybe I'm optimizing prematurely.
|
Apr 21 | 7:55 PM |
Michaelidle |
I'm guessing my issue may come from the fact that my reloading or rebuilding of the list could happen to be called twice in some cases, but every time I rebuild I create a new thread.
|
Mark M. |
that certainly won't help matters, though it would not directly lead to this exception
|
Mark M. |
while I'm no MVC/MVP/MVVM/etc. expert, you might want to think of what the ArrayAdapter holds as being a view-model, independent of the model that you are maintaining on the background thread
|
Mark M. |
only update the view-model on the main application thread
|
Mark M. |
you're free to update the model on background threads
|
Michaelidle |
But it is synchronized. Hm. I will give it another shot. Out of curiosity what do you think of this "Provider class" returning a list via Collections.unmodifiableList(myList); Do you happen to know if it returns a brand new List? If so... then this should be an impossible exception to get.
|
Mark M. |
I haven't used unmodifiableList(), sorry
|
Michaelidle |
Any recommended ways to do this whole paradigm?
|
Mark M. |
well, "this whole paradigm" is kinda broad
|
Michaelidle |
Not trying to reinvent the wheel here.
|
Apr 21 | 8:00 PM |
Michaelidle |
Yeah, it's just loading from the db, into a list<>, putting that into the ArrayAdapter in the ListView.
|
Michaelidle |
But your recommendation is to think of the ArrayAdapter as the "View" layer more so than the "Model" layer?
|
Mark M. |
oh, certainly
|
Mark M. |
now, you could argue where in the view/controller or view/presenter dividing line it falls
|
Mark M. |
but it's not the model
|
Michaelidle |
Okay, I will try to refactor my code with those things in mind. Thanks
|
Mark M. |
in the next book update, I'll be covering a TinyTextEditor sample
|
Mark M. |
multiple tabs (and on Android N, multiple windows)
|
Mark M. |
mostly, it is an extended example of using the Storage Access Framework (ACTION_OPEN_DOCUMENT and kin)
|
Mark M. |
related to this discussion, you'll see the general pattern that I use:
|
Mark M. |
1. use a background thread of some form for the I/O (in this case, an IntentService)
|
Mark M. |
2. have the background thread post an event with the result of the data load on a greenrobot EventBus
|
Mark M. |
3. have the UI subscribe to such events on the main application thread
|
Mark M. |
4. have the UI update itself (e.g., populate the EditText)
|
Mark M. |
so far, that basic pattern has been working well for me
|
Apr 21 | 8:05 PM |
Michaelidle |
What's the ETA of that update. Days weeks or months (just so that I keep my eyes peeled)
|
Mark M. |
first week of May
|
Mark M. |
probably Monday, May 2
|
Michaelidle |
Great, Thanks.
|
Apr 21 | 8:15 PM |
Michaelidle |
So another question for the night and then I'm done. I've been doing java for a while, but really haven't gone into multiple threads. It's mostly because my first job had me doing some interesting stuff, including some legacy code, which was a huge single threaded application. So now I'm doing Android and I've got a few threads in the application I'd like to start right when the application starts. For instance... in App.OnCreate() I'd like to create a "forever running thread" that I can just pass in db commands. I don't want the churn of creating a plain old java thread every time I want to do database work (which is basically all the time in my current application). I'm creating, updating, deleting and querying fairly often. Is there a standard way to do this? I have Java Concurrency in Practice sitting on my desk (Just came in on Monday).
|
Mark M. |
I'd use Executors.newSingleThreadExecutor()
|
Mark M. |
this will give you an Executor with a work queue and a single thread monitoring that queu
|
Mark M. |
er, queue
|
Mark M. |
you can then put Runnables on that queue, to have the thread do the work
|
Apr 21 | 8:20 PM |
Mark M. |
and, when there is no work to be done, the thread is blocking on the queue, and so it does not consume CPU time
|
Michaelidle |
That sounds excellent. I'll start that bad boy up in the onCreate and take it from there.
|
Apr 21 | 8:25 PM |
Michaelidle |
Have a good night Mark. Thanks for the advice. Looking forward to that book update!
|
Mark M. |
have a pleasant evening!
|
Apr 21 | 8:30 PM |
Michaelidle | has left the room |
Mark M. | turned off guest access |