Android volley image caching questions - java

in the google's own volley image cache tutorial
// Returns a cache size equal to approximately three screens worth of images.
public static int getCacheSize(Context ctx) {
final DisplayMetrics displayMetrics = ctx.getResources().
getDisplayMetrics();
final int screenWidth = displayMetrics.widthPixels;
final int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
final int screenBytes = screenWidth * screenHeight * 4;
return screenBytes * 3;
}
the recommended cache is three screens worth of images which equals to 7mb. I have an social media app and there is a newsfeed inside it.
1-) My first question is what will happen after the cache is full?
2-) I am thinking about removing cache every one hour and thus the cache will include the newer content. Is that reasonable ? What is the image caching logic behind the apps which includes something like newsfeed(for example, instagram)?
3-) How can i remove the old cache of specific item and force it to download it again? I tried this solution but it did not work:
VolleySingleton.getInstance().getRequestQueue().getCache().remove(IMAGE_URL);
mNetworkImageView = (NetworkImageView) getView().findViewById(R.id.networkImageView);
mImageLoader = VolleySingleton.getInstance().getImageLoader();
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
There are a lots of clone question of my third question but none of them has been answered.
Thanks for your helps. :)

1.) There are 2 layers of cache in Volley, one is the in-memory cache (in RAM) and the other one is a disk cache. Once a cache is full, the oldest image (meaning the image that hasn't been accessed the longest) in that cache will be evicted when a new image is about to be cached to make room for the new items. When something is evicted from the in-memory cache, it is still present in the disk cache and can be loaded very quickly from disk if it is needed again. If an image is evicted from the disk cache, it would have to be redownloaded if it's needed again.
2.) This doesn't sound reasonable once you understood the answer to question 1. The cache automatically makes room for newer content and there is no reason to evict content manually. Manual eviction will in fact lower your cache's efficiency.
3.) Broadly speaking, this is not possible (without hacks), because it should not be needed. If an image resource (almost) always expires after a certain time, the server should announce this using HTTP headers when sending the resource to the client. For example using the max-age property of the cache-control header. There are lots of websites explaining this in detail, for example: http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/. If an image resource almost never expires, you can consider changing its filename upon change and store that filename as a property. For example a user can have an avatar property containing the URL to the avatar. The avatar image can be cached indefinitely and you change the URL of the image if a new avatar gets uploaded.

For you 3rd question, I suggest that you read the following Google's documentation:
Request an Image
ImageRequest—a canned request for getting an image at a given URL and
calling back with a decoded bitmap. It also provides convenience
features like specifying a size to resize to. Its main benefit is that
Volley's thread scheduling ensures that expensive image operations
(decoding, resizing) automatically happen on a worker thread.
So if you use a ImageRequest only, you can refer to my answer at the following question:
Tell Volley not to use cached data but to initiate a new request?
Also in the Google's documentation:
ImageLoader—a helper class that handles loading and caching images
from remote URLs. ImageLoader is a an orchestrator for large numbers
of ImageRequests, for example when putting multiple thumbnails in a
ListView. ImageLoader provides an in-memory cache to sit in front of
the normal Volley cache,...
If you use NetworkImageView, you can refer to my answer at the following question:
Disable or Remove Cache in NetworkImageView- Volley
In which you will find that I use the following code inside VolleySingleton class:
mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
#Override
public Bitmap getBitmap(String url) {
return null;
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
}
});
Hope it helps!

Related

Suggestions to load data faster

I'm seeking for solutions to load 20 items from SQLite faster than 5 seconds (which is the seconds that I'm loading right now.) - First of all, I'm using a custom Listview Adapter.
I load 5 items in 1 second. I tried to load 20 items and I load them in 5 seconds.
This are the fields I retrieve from database: int, String, String, int, Bytes, float, int.
As you may think, after getting the bytes I convert them into Bitmap.
Bitmap image = convertBlobToImage(cursor.getBlob(4));
// Using this function:
public Bitmap convertBlobToImage(byte[] value){
byte[] new_value = Base64.decode(value, 0);
return BitmapFactory.decodeByteArray(new_value, 0, new_value.length);
}
So, in my class fields, they are going to get not the bytes but already the bitmap.
One of the reasons of the amount of time to read, is probably the bitmap. I just did a test on Paint. I saved two equal images one in BMP and another in JPG. The JPG image have the size of 2,87KB and the BMP 420KB!!
With the code above, is that result I'm getting? And probably one of the solutions could be: http://www.mkyong.com/java/how-to-convert-byte-to-bufferedimage-in-java/ ?
What do you guys think? Thanks.
Edit:
I was searching and I found about onDestroy(). Also I didn't have the "runOnUiThread" implemented, and I put that way. But I think it didn't give me any better result. What do you think? Could this increase the performance?
#Override
protected void onDestroy() {
super.onDestroy();
listView.setAdapter(null);
}
// And I also tried to put runOnUiThread:
runOnUiThread(new Runnable() {
#Override
public void run() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
DatabaseHandler db = new DatabaseHandler(Produtos.this);
display_products = db.get_all_oc_product(extras.getString("category_id"));
listView = (ListView) findViewById(R.id.product_listview);
inputSearch = (EditText) findViewById(R.id.product_inputSearch);
adapter = new itemAdapter(Produtos.this,R.layout.row, display_products);
listView.setAdapter(adapter);
}
}
});
Edit (2): I managed to decrease the time for 3 seconds on displaying 20 items. I accomplish this by closing all the connections to database after the queries. I was not doing this properly.
Correct way:
cursor db.query(...)
try{
// Code
} finally {
cursor.close();
db.close();
}
Edit (3): Further than the solution on Edit (2), one of the issues I had, which I was identified with, was the problem of the images.
So, I started to look at them and I saw images the 2000x1800 and 300kb and even more, and I found rapidly that was here the problem.
So, in the PHP webservice I developed a function to resize the images to half and converting them to jpg.
function resize($filePath){
list($width, $height) = getimagesize($filePath);
$percent = 0.5;
$newwidth = $width * $percent;
$newheight = $height * $percent;
$thumb = imagecreatetruecolor($newwidth, $newheight);
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
$source = null;
if($ext == "png"){
$source = imagecreatefrompng($filePath);
}else if($ext == "jpg" || $ext == "jpeg"){
$source = imagecreatefromjpeg($filePath);
}
// Resize
imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
// Output
$temporary = "C:\\xampp\\htdocs\\MyExample\\images\\temporary\\" . basename($filePath);
imagejpeg($thumb, $temporary . ".jpg", 50);
ImageDestroy($thumb);
return $temporary . ".jpg";
}
With this solution, I decreased the time for a stunning 1 second loading 47 items!!
I'd recommend checking into one of the java.nio.* packages to load your images. NIO is non-blocking which means you can load images asynchronously, resulting in more work done per time.
NIO also used native buffers on the system (outside the jvm) so it can prevent java from having to read things into it's heap, which again, makes things faster (native). There's a lot more benefits from using NIO here as well.
Well It's a bit hard to help from the other side of this question, but some generic tips:
Use profiling of some sort to determine which part is slowing you down. Is it loading the actual cursor back that's a problem? or is it the byte conversion? It's hard to say unless you measure - otherwise you could waste a lot of time optimizing something that isn't slowing you down. The simplest profiling example:
long startTime = SystemClock.elapsedRealTime();
suspectedSlowFunctionCall();
long endTime = SystemClock.elapsedRealTime();
Log.d(TAG, "Method duration: " + (endTime - startTime));
As Mr. Murphy mentioned in the comments, there are also tools that help you with this task. The android developers site has a great post showing how to use traceview, which is included in the SDK.
General wisdom is to not store Images in the database if you can avoid it. It is a lot easier on you to debug and work with (and for the system to load) if you store a file path instead and save the file.
Android's BitmapFactory should be able to handle JPEGs. If you want to use JPG, feel free
If something is unfixably slow, don't do it on the UI Thread! use something like an AsyncTask to do the slow operation, and show your results to the user one by one as they get loaded. This is always preferable to an operation that stalls whilst the user waits confused.

Keep the ImageView's Final View while scrolling

I am using Universal-Image-Loader to handle the display of my images, I am using a gridview like implementation in showing the images. Before the images are shown it will be downloaded first and cached into the sd card. While the images are still being downloaded, I handled the view with lazy list implementation for each images.
Problem:
After all the images are downloaded and ready for showing and
every time I scroll the gridview, up and down direction the images are somewhat being downloaded again with lazy list,
I believe it is being downloaded from the cache folder(sd card) of my application. Is there any way that I can keep the images(final state) while I am scrolling? Such that I don't need to downloaded it everytime I scroll to attain smooth scrolling.? I need to wait until the image is fully loaded from the sd card so that I can have smooth scrolling.
I got this setting from UIL, but I don't see any effect on this.
.resetViewBeforeLoading(false|true)
edit
I also tried this,
imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(getActivity()));
same as below
HttpParams params = new BasicHttpParams();
// Turn off stale checking. Our connections break all the time anyway,
// and it's not worth it to pay the penalty of checking every time.
HttpConnectionParams.setStaleCheckingEnabled(params, false);
// Default connection and socket timeout of 10 seconds. Tweak to taste.
HttpConnectionParams.setConnectionTimeout(params, 10 * 1000);
HttpConnectionParams.setSoTimeout(params, 10 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
ImageLoaderConfiguration config =
new ImageLoaderConfiguration
.Builder(getActivity())
.threadPoolSize(3)
.memoryCache(new WeakMemoryCache())
.imageDownloader(new HttpClientImageDownloader(getActivity(), new DefaultHttpClient(manager, params)))
.build();
imageLoader.init(config);
options = new DisplayImageOptions.Builder()
.showStubImage(icon)
.resetViewBeforeLoading(false)
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.cacheInMemory(true)
.cacheOnDisc(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
I tried experimenting, on the settings still the same.
What you should do is, you need to cache images in some directory like our application cache. Where images resides instead of memory cache. Using File caching will not increase Heap size and images will fetch from directory immediately after download.
File cacheDir = new File(this.getCacheDir(), "directory_name");
if (!cacheDir.exists())
cacheDir.mkdir();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
YourActivity.this).threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 3)
.denyCacheImageMultipleSizesInMemory()
// .memoryCache(new UsingFreqLimitedMemoryCache(2000000)) // You
// can pass your own memory cache implementation
.memoryCacheSize(1048576 * 10)
// 1MB=1048576 *declare 20 or more size if images are more than
// 200
.discCache(new UnlimitedDiscCache(cacheDir))
// You can pass your own disc cache implementation
// .defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.build();
imageLoader = ImageLoader.getInstance();
imageLoader.init(config);
imageLoader.clearMemoryCache();
imageLoader.clearDiscCache();
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.avatar)
.showImageForEmptyUri(R.drawable.home_shortcut).cacheInMemory()
.cacheOnDisc().build();
Note: I have used old version of UIL.
Purpose
imageLoader.clearMemoryCache();
This will clear memory when you run this code again. This is useful when your URL is same but image is different.
imageLoader.clearDiscCache();
This will clear disk before starting downloading to directory. This is useful when your directory has old images.
Memory Cache vs Disk Cache
Heap means your application memory allocation. If you don't provide disk cache, suppose your images have fix sizes of 100KB, 10 images has size of 1 MB will be stored in Memory instead of disk. While disk is not memory. It is a directory, physically on internal disk, has its own space inside. So instead of providing memory caching, disk cache is good as per memory utilization. Yes it is true that it is little bit slow but this will never effect your performance.

What is the correct way to handle the AQuery (android-query) download result?

Sorry if I missed something obvious, I tried really hard but didn't find any clue to answer my question.
So, my task is to download several images using their URLs and store them in some folder. I tried different approaches but for several reasons I decided I should use AQuery for this.
The download is actually done in IntentService which is started from the activity or from AlarmManager. The part where I download images is a for loop for downloading the required amount of images from the generated URLs:
for (int i=0; i < required; i++) {
url = makeURL(i);
aq.download(url, imgDir, new AjaxCallback() {
#Override
public void callback(String url, Object object, AjaxStatus status) {
super.callback(url, object, status);
File file = (File) object;
if (file.length() < 300 * 1024) { //just an example test
file.delete();
}
}
});
AQuery starts a separate thread for each download so the code continues to execute before I can get any result. The problem is that images can be broken or do not fit for other reasons so I do the test after each download in a callback method. If the image does not pass one of the tests I delete the file and need to redownload the image from another URL. But since all downloads are done in an independent threads I can't catch the result of the downloads in the same IntentService where I have all the "spare" URLs.
Since my IntentService is a background thread itself I think I can do all the downloads in that thread, check the file after each download and try another URL if it's bad.
As I understand, I have two options here:
find a way to make aq.download use the same thread
don't use AQuery and take another approach for downloading
I would be very grateful for an advice. My experience is still very limited to make the correct decision. Thanks in advance!

Display Video thumbnails from URL

Okay this might be a stupid one but kindly pardon me because I'm new to Android. I'm working on an App in which I want to display video thumbnails using remote URL's like:
Video URL:
http://173.193.24.66/~kanz/video/flv/9.flv
.JPG URL:
http://173.193.24.66/~kanz/video/Images/9.jpg
I have got both the video URL's and Image File URL's that I want to display on thumbnails stored on SQL database. The only thing is I don't know how to put them in List view inside a ScrollView. I tried searching on the Internet but they all seem to give tutorials on how to display video thumbnails from sd card path. I want to use any of these URL's to display video thumbnails.
I heard about API's but I can't use Youtube API since Youtube is banned in my country. If anyone knows any useful API or any other hack around, let me know. It would be highly appreciated. I'm using Gingerbread.
Add these lines to your Module:app gradle:
dependencies {
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
}
// pass video url into .load() method
Glide.with(context)
.asBitmap()
.load(/*Video Url*/)
.into(/*Name of Imageview*/);
Listview has its own scroll. So do not put listview inside a scroll view.
Use a custom listview to display the thumbnail.
Get your urls form the database.
http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
http://www.youtube.com/watch?v=wDBM6wVEO70. The talk is about listview and performance.
Use a viewholder.http://developer.android.com/training/improving-layouts/smooth-scrolling.html.
If you are displaying lot of images in listview consider using one of the below libraries.
1.Universal Image loader. https://github.com/nostra13/Android-Universal-Image-Loader.
2.Lazy List. https://github.com/thest1/LazyList.
Both use caching.
For univarsal Image Loader
In your adapter constructor
File cacheDir = StorageUtils.getOwnCacheDirectory(a, "UniversalImageLoader/Cache");
imageLoader = ImageLoader.getInstance();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(a)
// You can pass your own memory cache implementation
.discCache(new UnlimitedDiscCache(cacheDir)) // You can pass your own disc cache implementation
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.enableLogging()
.build();
// Initialize ImageLoader with created configuration. Do it once.
imageLoader.init(config);
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_id)//dummy image
.cacheInMemory()
.cacheOnDisc()
.displayer(new RoundedBitmapDisplayer(20))
.build();
In your getview()
ImageView image=(ImageView)vi.findViewById(R.id.imageview);
imageLoader.displayImage(imageurl, image,options);
Those tutorials are correct but you're missing a step. You need to download these images to the device first using the URL. Then you can set them up to be viewed in a listview.
See how to do this in these threads:
How to load an ImageView by URL in Android?
Android, Make an image at a URL equal to ImageView's image
There is a popular open source library that handles downloading and caching of images automatically. Check it out:
https://github.com/koush/UrlImageViewHelper

Update photo in flex without blinking

i am trying to simulate a live view using a canon Camera.
I am interacting with the cam using the CanonSDK, i get an image every a short period in order to simulate a video frame by frame. This works fine, i am using java to do the backend and send the images trough BlazeDS to flex.
The problem is not getting the image, the problem is that when i load a new image using something like:
image.source=my_new_image;
the new image is loaded but it produces a short white blink and it ruins the video...
So i would like to know if the is a way to update an image on flex avoiding the blinking problem, or if i could make a video streaming from java and pick it up with flex...
Thanks in advance!!!
The easy way is to use a technique called double buffering, using two Loaders - one for the image which is visible, and one for the image which is being loaded and is invisible. When the image has completed loading it becomes visible, and the other one becomes invisible and the process repeats.
In terms of efficiency, it would be better to at least use a socket connection to the server for transferring the image bytes, preferably in AMF format since it has little overhead. This is all fairly possible in BlazeDS with some scripting.
For better efficiency you may try using a real-time frame or video encoder on the server, however decoding the video on the client will be challenging. For best performance it will be better to use the built-in video decoder and a streaming server such as Flash Media Server.
UPDATE (example script):
This example loads images over HTTP. A more efficient approach would be to use an AMF socket (mentioned above) to transfer the image, then use Loader.loadBytes() to display it.
private var loaderA:Loader;
private var loaderB:Loader;
private var foregroundLoader:Loader;
private var backgroundLoader:Loader;
public function Main()
{
loaderA = new Loader();
loaderB = new Loader();
foregroundLoader = loaderA;
backgroundLoader = loaderB;
loadNext();
}
private function loadNext():void
{
trace("loading");
backgroundLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler);
backgroundLoader.load(new URLRequest("http://www.phpjunkyard.com/randim/randim.php?type=1"));
}
private function loaderCompleteHandler(event:Event):void
{
trace("loaded");
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var loader:Loader = loaderInfo.loader;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderCompleteHandler);
if (contains(foregroundLoader))
removeChild(foregroundLoader);
var temp:Loader = foregroundLoader;
foregroundLoader = backgroundLoader;
backgroundLoader = temp;
addChild(foregroundLoader);
loadNext();
}

Categories

Resources