I have ListView in Android. The list contains elements that are loaded from database. In this situation it is recommended to loading data in AsyncLoader.
But, I don't load all elements at once. I rather load a ( for example ) 20 elements and I load next 20 onScrollEvent. So, it is not user-friendly to show a ProgressDialog ( I have to do it) when user scroll.
Is it allowed to do it from UI thread? How to solve it?
So the main questions here are chunk size and where the database is. If the database is remote, you'll obviously need to run an AsyncLoader of some sort. If the database is local, and the number of items is small, you can probably get away with doing it on the UI thread. Personally I wouldn't even do it onScrollEvent- I'd use the getView of the Adapter.
if (getCount() - position < CHUNK_SIZE) {
//We're running low on items, lets get the next CHUNK_SIZE now
}
If you do end up doing an asynchronous load, that check should also make sure there isn't an ongoing load in progress. CHUNK_SIZE is kind of a guess- too big and you'll waste bandwidth, too small and the user can scroll to the bottom. You take a guess and adjust it. It can even be adjusted dynamically based on network speed if you decide you care enough.
Related
i have an appwidget which starts an activity as user clicks it.
In my Activity i have a gridview containing relatively small Drawables(Images, because drawable could be mor than just Images) but user can size them.
I noticed that it takes too Long to size them at runtime when Scrolling trough the gridview.
I want to prepare an lruCache only one time in my appWidget's onUpdate which is called at the very beginning when the user places the appwidget on the Screen.
The problem
When i define an lruCache in my appWidget with
private final int lruCacheSize = (int) (Runtime.getRuntime().maxMemory()/1024);
private LruCache<String, BitmapDrawable> myLruCache;
...
myLruCache = new LruCache<String, BitmapDrawable>(lruCacheSize) {
#Override
protected int sizeOf(String key, BitmapDrawable value) {
// TODO Auto-generated method stub
return super.sizeOf(key, value);
}
};
Will it only exist as Long as the process exist of the appWidget? Or does the Cache-File from lruCache stays in my cacheDir of my app? Or will it be deleted after the process of the appWidget is finished? If it does exist over the process-lifetime of my appWidget, how can i Access it from my Activity?
I don't want to create everytime the user clicks on the appWidget a LruCache and fill it up with all the relatively small Images the gridview will Need later. I want to do it once, or if user clears the Cache which will be checked every hour(to save battery).
The Question
How can i achieve that? Or is there a much better/ simpler way.
Time is not really (if it happens once at the very beginning) the Problem, i notify the user that the Cache is being prepared when placing the appWidget on the Screen.
Any help is appreciated.
Update regarding CommonsWare answere
Use Traceview to determine specifically why your implementation is slow.
The app Scrolling is slow because i provide three sizes (small, medium, large) and i scale them at while Scrolling trough my gridview.(Because it would take several seconds to scale them once at activity Startup, so i don't want that).
See here what i do in my imageview which will later Show the drawable(which is an appIcon):
android:scaleType="fitXY"
android:adjustViewBounds="true"
This causes the lag because i do this for everyimage depending if selected scale size is small medium or large.
There is no "Cache-File" in your code.
So then i i'm misunderstanding something. Doesn't the lruCache create an Cache-File in the Cache-Directory of my application? If not how does in works?
First, you cannot force the user to install the app widget. Work on solving the actual performance problem, rather than trying to build some optimization that will not help all users.
My "app" is only an appwidget. There is not appIcon like at FaceBook. It is only an appWidget when he doenloads my app. Which starts an activity when you click on the button.
Second, your process can readily be terminated milliseconds after onUpdate() completes. Do not fill a cache, only to have it never be used.
I want to use the Cache which i want to fill with the Drawables in the onUpdate of the appWidget, and then i want to use These Drawables from the Cache in my activity. So i don't understand why i never would use the Cache? Maybe i'm misunderstanding something.
Picasso would give you better performance from the user's standpoint.
Does it fit my Needs after the update right now?
---------------------------------------------------------------------------------------------
Update 2
Since the scaling should be done by the GPU and take microseconds, I have difficulty believing that is your problem. What specifically did Traceview show you that made you think that scaling has something to do with this?
I noticed that the Scrolling is very fluid at medium size because that's nearly the origin size of the appIcon from the PackageManager. If scroll trough large, where only 2 or 3 appIcons are displayed per row (at medium there are 5 or 6 displayed but it is much more fluid)m it lags. (With the same logic behind it). So the only Logical answere can be scaling in the XML at the ImageView. As i commented that XML-Scaling out and scaled the appIcons to the size Large and put them directly scalled to to my Adapter for GridView it runs really smooth( Only one or to really small lags at the beginning because convertView is null??)
onUpdate() will be called much more frequently than the user will actually use your app widget.
That's right after every onclick or at the specified time. But i check if the Cache has been cleared or not, if not don't Change anything, if so load Drawables to Cache.
I noticed that it takes too Long to size them at runtime when Scrolling trough the gridview.
Use Traceview to determine specifically why your implementation is slow.
Will it only exist as Long as the process exist of the appWidget?
It will only exist for the lifetime of the process of your app.
Or does the Cache-File from lruCache stays in my cacheDir of my app?
There is no "Cache-File" in your code.
I don't want to create everytime the user clicks on the appWidget a LruCache and fill it up with all the relatively small Images the gridview will Need later. I want to do it once, or if user clears the Cache which will be checked every hour(to save battery).
First, you cannot force the user to install the app widget. Work on solving the actual performance problem, rather than trying to build some optimization that will not help all users.
Second, your process can readily be terminated milliseconds after onUpdate() completes. Do not fill a cache, only to have it never be used.
Or is there a much better/ simpler way.
Use Traceview to determine exactly where your problem lies. Then, solve that problem. For example, the problem could be that you are loading these images on the main application thread, and using a library like Picasso would give you better performance from the user's standpoint.
The app Scrolling is slow because i provide three sizes (small, medium, large) and i scale them at while Scrolling trough my gridview
Since the scaling should be done by the GPU and take microseconds, I have difficulty believing that is your problem. What specifically did Traceview show you that made you think that scaling has something to do with this?
Doesn't the lruCache create an Cache-File in the Cache-Directory of my application?
No.
If not how does in works?
It is an in-memory cache.
So i don't understand why i never would use the Cache?
onUpdate() will be called much more frequently than the user will actually use your app widget.
I am bulding up a grid of images for an app I'm building. It works like so:
Build up a list of Image IDs, which I must query using a different content provider each (these are images from MMS threads)
Create new activity, which hosts an ImageGridFragment. This fragment has a custom adapter that takes the grid of images, and loads each one as a bitmap asynchronously.
After images are loaded, they are cached in an LRU cache so I don't need to run unnecessary computation
So far, everything works quite well. However, I would like to pre-buffer images so that when the user scrolls down, s/he doesn't have to wait for images to load. They should already be loaded. The stock Android Gallery accomplishes. I've had a look at the source, but think there must be a more straightforward way.
To answer members' questions
Images are loaded one by one using the content://mms/part/xxx, where xxx is the ID of an image. These are MMS images, and to my knowledge, cannot be loaded as a batch process (though, maybe I'm wrong). I use a content provider in an AsyncTask to load each image
I've tried the following:
Pre buffer 30 images or so right when the fragment is created. This is not ideal because the massive I/O request, actually prevents the on-screen images from loading quickly (but the buffering does work well!)
Detect when the requested view to load is at the very bottom-right hand corner of the screen, which could work, but then would fail in the case that the GridView takes up only part of the screen. It also seems like there should be a cleaner way to do this
Thought about, but did not try, an OnScrollListener, but this will not pre-buffer images until I start scrolling, which is not ideal
So, my questions are:
Is there a good way to detect when the last GridView item is requested to load? I found that the GridView.getlastvisibleposition() method is not useful here, because it is actually returning the last element for which Adapter.getView() has been called for. If I can do this accurately, I can launch the buffer request at that time
Is there a better way to do this?
you can do right this
if(GridView.getlastvisibleposition() = mAdapter.count()-1)
how you load the images?
is it from URL or from sdcard?
are you using a image loader library?
I'm making an ListView in my app which over time could contain hundreds of items. Are there any "best" methods of loading lots of data?
My idea is to load it in chunks (say 10-20 items). Load the first chunk, then when the user is about halfway through scrolling, load the next chunk, add it to the bottom of the list (and make sure the list scroll offset doesn't jump about).
Some other ideas I had just didn't like so much were accepting the cost of a large http call and load all the data at once, but just load it in chunks as they scroll, or maybe add a "Next x items" button at the bottom, or loading all the items into the list at once and having one large list I don't need to keep track of.
I personally like my original idea, I was just wondering if there is a preferred method or doing this, and if there are any performance issues I could have.
The data in question will be a JSON string, and each item will display some title text, a date, the author of the item, and an image which will be downloaded using the Picasso library.
Your initial idea is my preferred approach because it works very well in most situations.
The second one may work well, but the problem is, the "large" data concept is relative across devices. For powerful devices you may load 2000 items at once, but it will kill older, slower phones. Also, if you're loading 2000 items when the use case of that ListView is to choose one in the first 100, you are wasting bandwith.
The first approach is very scalable: You really don't care if there are 5 items or 50 million, you just load chunks as the user consumes them. The memory usage is consistent. Coupled with ListView's view recycling, this will have a small memory footprint.
To say something positive about the second approach: Maybe in a use case when the ListView always has the same data, and it rarely changes, for example, an image library, you may want to load all the data at application start and cache it, so you never have to do network requests while the user is using the app. If the data size is not huge, I'd go for this second approach. But always having in mind that there's a critical size after which you will need to page!
Basically you can load all items once and show them all (if you will use ViewHolder pattern and lazy image loading using Picasso - everything should be ok). If you have some business logic which force you to show data by pages - you can follow the way that you described first.
If you will show all data - you can add search by list for better UX(you also can do it when you have pages but it will be more tricky).
Some time ago I wrote an article on similar topic:
http://developer.samsung.com/android/technical-docs/Batch-loading-list
The article comes also with a small library of common classes.
Maybe you will find it helpful.
Cheers,
Suppose I want to show items in a ListView. The datasource is a REST API, let's say www.example.com/getItems?ps=10cp=1
where ps = pagesize and cp = currentpage.
The answer gives count = xy, amount of total items.
Now the amount of total items is, let's say 1000.
What's the best way to load and show those in a ListView? Load all 1000 items from the API, throw them in the ListAdapter? I watched a Google IO talk where they said loading one big chunk of data over the network is better than loading lots of small chunks.
Another possibilty would be to bind every ListItem to the API (kind of like lazy loading images), but that would create a call to the API for every single ListItem, that seems stupid.
A compromise would be to load 100 items, then load 100 more when the user has scrolled to the 100th item, is there some sample implementation to look at? Is that the 'way to go'? What's the best practise here?
Yes I agree with you that it is the way to go.
you can use endlessadapter or this one.
for smooth scrolling the loading of next batch should happen before the scrolling end.
In my Android App I have a listview containing 30 rows, and each row consists of several textviews of which one is spannable and sometimes contains a lot of formatted text and images.
Those images are loaded from the web asynchroneously: A placeholder is displayed until the image has been downloaded is then replaced by the image.
Unfortunately, the rows of the listview are loaded when I scroll over them. This makes the whole thing very slow. Also, those images are loaded from the web again and again, whenever I scroll over the row.
How can I turn it off, that the ListView rows are loaded when I scroll over them? They should be loaded once when I start the activity and never again.
Best regards and thanks in advance,
Jan Oliver
When you do a lazy-loading ListView, is because you want to speed it up your app. Turn it off is not the best solution. So, what you can do is implementing a basic cache system in order to avoid downloading and setting the ImageView again and again.
The easiest way to do so is implementing a HashMap with URLs as keys and Bitmaps as values. Something like this:
Map cache = new HashMap();
// then, on your lazy loader
Bitmap image = cache.get(urlOfTheImage);
if( image == null ){
// download and decode the image as normal,
// then assign the decoded bitmap to
// the 'image' variable
cache.put(image);
}
imageView.setImageBitmap(image);
If those images will be the same always, meaning that each time you open the app the same images will be downloaded, then you better save those images in the filesystem and use them from there.
On the other hand, if the images tend to change, you could implement some interesting stuff: use SoftReferences. There's an explanation in this video. This can also be used if you are loading images from the filesystem.
Edit
With regards to your comment, I highly recommend you watching the video I posted. It's one hour long, but it really worths the effort. When using an adapter, checking if the convertView is null is just a simple way to improve performance, though there are some other techniques that will improve your app even more. Also, if you had have problems while using that trick, is because you are probably implementing it the wrong way. Remember: even if you don't re-inflate the views, you do have to set the value of each one of the children views, otherwise you will experience some problems.
If you can, start with an Image Array full of the "placeholder images", then download the images in to an Array firing an AsyncTask during on Create. During row view building just refer to the array. That way if it has the new image it will load it, if not it will get the placeholder.
If you have a lot of data its gonna get real slow and be a crappy expirience for the user.
Create a list of objects that represent each row. Create a loader as a background thread that updates the objects as it loads the data. Your list view will draw data from the objects.
(Not a good idea if you have hundreds of rows and a huge amount of data in each row - in that case, you should only load data within a few rows of the currently active row and have some sort of MRU cache).