My activity is to download and display announcements (from announcements web-service). In order to keep UI responsive I do following actions in background threads:
1) upload XML (data are stored in XML),
2) process XML,
3) upload images.
Besides, I change priorities of UI and background threads to keep UI thread responsive:
in main thread:
Thread.currentThread().setPriority(10);
in background threads:
Thread.currentThread().setPriority(1);
Each announcement is put into a TableRow and each TableRow is put in TableView using handler.
When user scrolls the TableView and is about to reach the end of the table, new rows are added and HTTP-GET for next announcements is sent (+ parcing (using XPath) + image uploading + filling table rows with content).
My problem:
when uploading and parsing is in process user interface is only partially responsive (there are a number of lags 0.1-0.5sec each when user scrolls TableView).
I want to make absolutely smooth scrolling which doesn't depend on data uploading/processing.
What has been done in a wrong way and what else can be done here?
For example, if I diminish number of operations in handler, it should improve responsiveness... maybe like that. However, I see no heavy and resource-consuming instructions there.
UPDATE
I rewrote my code using ListView instead of TableLayout, since the former is considerably faster (as it turned out, I didn't know that). But I still have spikes when XML processing and ListView scrolling were going simultaneously. I made 2 experiments:
1) I removed all background processes - new rows were added to ListView upon scrolling down, but no data were processed for those rows (so they remained empty) - such variant worked great, no any spike, UI absolutely responsive.
2) 2nd experiment was to remove calls to ListView updates (XML file was processed but data found weren't passed to ListView adapter) - that time I had spikes. So I conclude those spikes are due to background threads and are not due to UI update calls.
How can background threads slow my app if I make them have lower priority than priority of main one?
Is
Thread.currentThread().setPriority(some_int);
a correct way to set thread priority?
At some point, the UI thread needs to add these new objects, create new UI objects in memory, and add them to the draw list.
What you could try is to pre-allocate as much memory as you need by making a lot of invisible Table Rows in your UI. This might solve some issues, as potentially the UI thread is trying to reallocate it's memory to make room for your new TableRows.
Also, you could try to use Thread.yield(), in your background threads. This informs the android OS that this thread is ready, and if needed allows the resume of the UI thread (if the UI thread is idle, it resumes instantly).
If you're not doing any I/O on your UI thread, then the lags you describe are probably layout passes. Suggest you focus on optimizing your layout. Is there any reason you're not using a ListView?
Related
I have an android app that I'm developing using Xamarin. The app contacts the server and, via web service (SOAP), receives a list of objects. Currently, in my axml file I have just linear layout (ll) within scrollview tags and nothing else. In the code, I loop through the collection and new up the elements that I want and attach it to a layout. Once I'm done with each record, I attach (i.e.AddView) to the master layout (ll). Everything works.
I have a couple of concerns and I appreciate some feedback on it.
1) Each object in the list contains an URL to an image online. Currently, for each object, my process downloads the picture individually. Would ListView give me any advantage of reusing (caching , etc.) an already downloaded picture even though other attributes of the objects are different? Will there be any gain in terms of network utilization if I switch to ListView?
2) Is drawing elements by hand (like I'm doing) an acceptable best-practice?
Thanks all.
Definitely use a ListView. There is a great article here by Lucas Rocha that outlines exactly why ListViews are beneficial and how to make them perform even better. To give you a few examples, ListViews minimize the number of view inflations you do, and they only create the list items currently visible on the screen or about to become visible on the screen.
This is a huge improvement from your approach, since your current method would load every element in the list before presenting the activity to the user. Therefore, drawing elements by hand like you're currently doing is definitely not best-practice.
Also, for displaying images from URLs in your Xamarin app, I highly recommend that you use the Xamarin component UrlImageViewHelper. Despite being incredibly easy to implement in your app, it will improve performance drastically since it takes care of image caching and async image loading.
With a complex adapter, GridViews and ListViews can sometimes take a long time to populate after calling the setAdapter() method. Is there a method we can override that is triggered when the view has been successfully populated with data and is ready to be shown?
My feeling is that it isn't the setAdapter() that takes a long time, but rather that the complex adapter is doing something complex. The ListView and GridViews are specified to work rather quickly, but there's a few reasons they might be slower. Here's a few thoughts as to how to work this one out.
Figure out what's taking so long. Network calls, large database queries, and loading large files (Bitmaps in particular) are often the guilty party. If you don't have any of those, try profiling your code, and see what you can gleam from that.
Once you've figured out the culprit, see what you can do to decrease the amount of time. Perhaps shrinking the size of a bitmap, or using a better database query would work.
If you can't shrink the time down any more, then do the complex task in an AsyncTask. This will do the complex part of your code in the background, and only show the result when it is complete. This works well for things like network calls, loading bitmaps, etc.
I'm reading chapter processing bitmaps off the UI thread in Android training.
In this chapter, the author talks about handling concurrency for GridView when used in conjunction with AsyncTask:
Common view components such as ListView and GridView introduce another
issue when used in conjunction with the AsyncTask as demonstrated in
the previous section. In order to be efficient with memory, these
components recycle child views as the user scrolls. If each child view
triggers an AsyncTask, there is no guarantee that when it completes,
the associated view has not already been recycled for use in another
child view. Furthermore, there is no guarantee that the order in which
asynchronous tasks are started is the order that they complete.
For the above paragraph, I have two questions:
(1) What's the child view of the GridView?
Take the following figure as an example, is every grid a child view?
(2) I'm confused by "If each child view triggers an AsyncTask, there is no guarantee that when it completes, the associated view has not already been recycled for use in another child view."
Can anyone explains more detail? For example, grid[1,1] triggers an AsyncTask, when the AsyncTask finishes, why there may incur a problem?
Yes, Each grid item is a Child View.
The main purpose of an AsyncTask is process long-running jobs off the main thread so the UI doesn't hang. Internally, an AsyncTask uses a ThreadPoolExecutor with a fixed number of threads to manage tasks. So even if you fire off twenty AsyncTasks, only a few of them will be executing at a time. At the same time, since each AsyncTask runs in it's own thread, it might finish at any time depending on how the system decides to schedule your thread. So there really is no guarantee that the tasks will finish in the same order.
When you process images off the main thread, you also need to consider the following scenario.
In AdapterViews, we recycle the child views so that we don't need to initialize new Views every time a View is scrolled on screen. So what will happen in this case is that a Child View will trigger an AsyncTask to fetch the image when it appears on screen.
But before the image is fetched, the user continues scrolling and the view scrolls off the screen and appears again on the other side(because we are recycling the views). Now, it triggers another AsyncTask to fetch the image it is supposed to display. At the same time, the earlier AsyncTask associated with this view completes and sets the Image for this View, even if it isn't the right position for the image.
I want to develop a timeline view for Android, which is like a infinite scrolling Google Calendar day view.
Now I use a fixed length RelativeLayout in a ScrollView, and I think I should use AsyncTask to dynamically load the data.
I do not know if it is necessary to use AsynTask to load the data, because I just want to load some texts now.
My idea is to set two points near the upper and lower borders of the RelativeLayout and load data when scroll across the points. Should I prepare the child views in AsyncTask and attach them to the RelativeLayout in onPostExecute() or create a new RelativeLayout and then replace the old one in onPostExecute()?
What is the common practice? Thanks.
If you're loading the data from a static array or some other data source that is already in memory, you may be able to get away with doing it on the UI thread. If you're going to be loading the data from disk or network, you should (and in the case of network must) load it from a background thread (i.e. not the UI thread), and AsyncTask<> is a great way to do that.
Your approach seems reasonable. You may be able to memoize and reuse layouts as the user scrolls.
I have a TableLayout, within a ScrollView, which is updated constantly by an AsyncTask.
I don't know if it is "legal" to add/remove rows by the AsyncTask when at the same time the user may be scrolling the TableLayout.
How can I make sure the update does not interfere with the Scrolling in the GUI?
Or does the OS take care of this for me?
On Android, the main thread that your Application runs on is the only thread that can update the UI. If you try to modify the contents of your layout from within an AsyncTask, you'll get an error. AsyncTask's onPreExecute and onPostExecute methods, however, ARE run on the main thread, so you can update the UI there.
So, to answer your question, yes the OS takes care of this for you by not allowing asynchronous UI updates.