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.
Related
I'm making android app where I'm trying to generate video from bitmap images taken from TextureView. Here , I'm able to get the bitmap images from TextureView and able to save it in internal storage for later video generation. But Problem is as bitmap images are created and saved in local storage, App's Heap Size is also growing rapidly and later after saving around 800 images as .png file , it gives OutOfMemory Exception with Clamp target GC heap from 258MB to 256MB. So, How do i avoid Heap growing ?
I tried increasing app heap size but it's no use as heap is growing as new images are created and saved. I tried bitmap recycle and also setting null to bitmap, but no change in performance.
// code for getting bitmap from textureview
Bitmap bitmap = textureView.getBitmap(TF_OD_API_INPUT_SIZE, TF_OD_API_INPUT_SIZE);
//Bitmap that is displayed on screen
if(croppedBitmap!=null){
croppedBitmap=null;
}
croppedBitmap = Bitmap.createBitmap((int) canvasWidth, (int) canvasHeight, Bitmap.Config.ARGB_8888);
above code are periodically ran and called using Runnable#run()
When User presses record button below code get executed and as Texture surface get updated that is saved as bitmap locally
#Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
if(Utils.isVideoRecordingStarted(activity)){
//if it already has bitmap recycle it to create room
if(mToVideo!=null){
mToVideo.recycle();
mToVideo=null;
}
if(newImage!=null){
newImage.recycle();
newImage=null;
}
//hold bitmap image
mToVideo = textureView.getBitmap(VideoManagerConfig.VIDEO_WIDTH,VideoManagerConfig.VIDEO_HEIGHT);
if(mToVideo!=null) {
if(activity.getResources().getConfiguration().orientation!=ORIENTATION_PORTRAIT){
mToVideo = textureView.getBitmap(VideoManagerConfig.VIDEO_HEIGHT,VideoManagerConfig.VIDEO_WIDTH);
if(mToVideo!=null){
newImage = Utils.RotateBitmap(mToVideo.copy(mToVideo.getConfig(),true),90);
Log.i(ObjectDetectionCameraImpl.TAG,"W: "+newImage.getWidth()+", H: "+newImage.getHeight());
SaveToLocalStorage(newImage);
}
}else {
SaveToLocalStorage(mToVideo.copy(mToVideo.getConfig(),true));
}
mToVideo=null;
}
}else if(isInitilizedVideo && !Utils.isVideoRecordingStarted(activity)){
//video stopped and has something to encode
imageFromTextureListener.getBuffer(getBufferMetaData());
}
}
};
Following are the logs that are generated as images are saved.
VideoManager: images_924.png // means 925 images are already saved locally indexing started from 0
Buffer: 925 / 1800 //here 1800 => total images that it will be saved
Following the link to log file:
https://drive.google.com/open?id=1GxFV-h2V68FSoWRs9K1AYhZChnlTFFqq
A similar issue that occurred while trying to zoom an image (in java though) was answered here as following: unless garbage collection occurs, the JVM (android uses ART though) keeps expanding heap as and when it needs to, all the way to the full heap size given to it. In short,
a Java process is free to use all the memory you gave it.
I have save image path in database and image save in folder. Displaying image in list view app is stuck and slow. I not understand how to resolve this issue.
I am using following code:
strContactImage = customer.getPhotoPath();
Bitmap decodedImage = BitmapFactory.decodeFile(strContactImage);
customerImage.setImageBitmap(decodedImage);
customer.getPhotoPath() gets folder photo path image, and displays it properly, but when I added more than 20 records app slows down, and gets stuck.
Use uri to set image into image view instead of decoding into bitmap...
strContactImage = customer.getPhotoPath();
Uri uri=Uri.fromFile(new File(strContactImage));
// Set the uri into ImageView
customerImage.setImageURI(uri);
It might help you...
You can use any image caching library, here is the example of Picasso
strContactImage = customer.getPhotoPath();
Picasso.with(context)
.load(new File(strContactImage))
.into(customerImage);
check the size of the images they can effect the memory and make the app stuck and slow if they are take a lot of memory !
to fix that
take a look here
Load a Scaled Down Version into Memory
and check the method calculateInSampleSize() and decodeSampledBitmapFromResource()
it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.
and you can use library that can help you to load image and they will take care about the memory usage and loading the image into Your View
Library :
Picasso
example :
Picasso.with(context).load(file).into(imageview);
Glide
example :
GlideApp.with(context).load(file).into(imageview);
Tools - check this for more about :
TinyPNG - Smart PNG and JPEG compression
Optimizilla
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!
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.
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