Android App Consumes Memory - android

After alot of scrolling the memory goes up to:
I have simple android app that shows list if images but the memory appears to be high i'm using Picasso for loading images into the list and here is my code for loading images:
#BindingAdapter({"thumbnail_cars"})
public static void loadImage(ImageView imageView, Cars cars) {
Log.e("width ",String.valueOf((Converter.getScreenWidth()-40)*0.4));
Log.e("height ",String.valueOf(Converter.convertDpToPixel(130)));
float margins = Converter.convertDpToPixel(20);
String widthUrl = cars.getImage().replace("[w]",String.valueOf((Converter.getScreenWidth()-margins)*0.4));
String heightUrl = widthUrl.replace("[h]",String.valueOf(Converter.convertDpToPixel(130)));
imageDownloader.loadImage(heightUrl, imageView,R.drawable.ic_launcher_background);
}
Any help will be much appreciated

Related

Android: How to resuse or limit amount of Bitmap

I do have an app that shows images and some other text info from firebase-Realtime,
there is a problem with my app ram usage that it can easily cross over 400+mb
so i used profiler to check and solved every MemoryLeaks my app have, now when i checked the Android studio profiler i found that there is a ton of Bitmaps that are using loads of memory.
AlSO Iam using Picasso to load images into Recycleview from within Adapter
also using Glide sometimes
so i want to knew is it possible to solve these bitmap memory usage or is it possible to to limit number of bitmap that can be used, cuz every time a new image are shown the app would make a new bitmap and uses more Ram.
Adapter code to load Images into recyclerview:
public class HomeScreenWorkViewHolder extends RecyclerView.ViewHolder {
View view;
DatabaseReference likeReference;
public ImageView like_btn;
public TextView like_text;
public HomeScreenWorkViewHolder(#NonNull View itemView) {
super(itemView);
like_btn = itemView.findViewById(R.id.like_btn);
like_text = itemView.findViewById(R.id.like_text);
view = itemView;
}
public HomeScreenWorkViewHolder(#NonNull View itemView, OnItemClick callBack) {
super(itemView);
like_btn = itemView.findViewById(R.id.like_btn);
like_text = itemView.findViewById(R.id.like_text);
view = itemView;
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callBack.onItemClicked(getAdapterPosition());
}
});
}
public void setdetails( String name, String image, String description, String location) {
TextView mtitletv = view.findViewById(R.id.product_layout_name);
TextView mdesrcriptiontv = view.findViewById(R.id.product_layout_description);
TextView mlocationtv = view.findViewById(R.id.product_layout_location);
ImageView mImagetv = view.findViewById(R.id.product_layout_image);
mtitletv.setText(name);
mdesrcriptiontv.setText(description);
mlocationtv.setText(location);
Picasso.get().load(image).placeholder(R.drawable.ic_baseline_cloud_download_24).into(mImagetv);
}
Sounds like you are either keeping references to the bitmap around. If that is not the case, but you are trying to keep references in memory, but reduce memory footprint with changing the sample size.
Sampling size can be thought of as this: If you have a 1000x1000 image by 8 bits per pixel..
You have a 1,000,000 bytes of image loaded into memory. Lets say you only needed a thumbnail of 100x100. You could then change the sampling size to 10, and it would read every 10th pixel from the file, building a newer / smaller memory footprint image. This would then Go from 1,000,000 bytes to 10,000 bytes.
Try the answer on this SO question..
Strange OutOfMemory issue while loading an image to a Bitmap object
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

When using uri to load a large local image, it is very slow. How can I optimize it?

I want to implement the function of a local gallery. I first use MediaStore to get the uri of all local pictures, and then display it on the imageView through Picasso / Glide.
If the picture is small, the display speed is fast, but when the picture is large (3mb-4mb or more), the display speed becomes very slow, and the user experience is very bad.
My code is as follows:
// use MediaStore to get the uri of all local pictures
val cursor = APP.INSTANCE.contentResolver.query(
EXTERNAL_CONTENT_URI,
arrayOf(_ID, DISPLAY_NAME, DATE_ADDED),
null,
null,
"$DATE_ADDED DESC"
)
var images = ArrayList<EImage>()
while (cursor?.moveToNext() == true) {
val id = cursor.getLong(cursor.getColumnIndex(_ID))
images.add(
EImage(
id = id,
name = cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
uri = ContentUris.withAppendedId(EXTERNAL_CONTENT_URI, id),
added = cursor.getLong(cursor.getColumnIndex(DATE_ADDED))
)
)
}
// Display to imageView via Picasso / Glide
// Picasso
Picasso.get().apply {
isLoggingEnabled = true
}.load(imageUri).apply {
config(Bitmap.Config.RGB_565)
resize(400, 400) //
onlyScaleDown()
centerInside()
into(imageView)
}
// or Glide
Glide.with(imageView.context).load(imageUri).apply {
override(400, 400)
centerInside()
into(imageView)
}
When the local image is above 3mb-4mb, I found that if the resize width and height are relatively small (below 350), the loading speed is fast, but if the width and height are relatively large (above 350), the loading speed will become Very slow (20-30 seconds per image), I grabbed Picasso's log as follows:
May I have done something wrong? How can it be optimized? Thank you very much!

memory leak while handling bitmaps

I'm trying to free memory when I don't need it any more but that doesn't seem to be easy. I create a ListView containing some images (Bitmaps). Every time I do that, the memory monitor shows increase of memory used by my app. This is how I do it:
private ArrayList<ImgMdl> populateList(){
ArrayList<ImgMdl> list = new ArrayList<>();
File folder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + settingsFileDir + "/" + imgLogFileDir);
if(folder.exists()) {
File[] listOfFiles = folder.listFiles();
listOfFiles = sortFileList(listOfFiles);
for (int i = 0; i < listOfFiles.length; i++) {
String imgFilePath = listOfFiles[i].toString();
Bitmap bmp = BitmapFactory.decodeFile(imgFilePath);
ImgMdl imgMdl = new ImgMdl();
imgMdl.set_itm_txt(getDateString(listOfFiles[i].getName()));
imgMdl.set_itm_img(bmp);
list.add(imgMdl);
}
}
return list;
}
public class MainGate_ImgMdl {
private String itm_txt;
private Bitmap itm_img;
public String get_itm_txt() {
return itm_txt;
}
public void set_itm_txt(String itm_txt) {
this.itm_txt = itm_txt;
}
public Bitmap get_itm_img() {
return itm_img;
}
public void set_itm_img(Bitmap itm_img) {
this.itm_img = itm_img;
}
}
If I open the activity containing that list several times, I'll sometime get an outOfMemoryException. How can I free memory if I don't need it any more? finish() doesn't help.
First, do not load images until they are needed. Suppose that you have 100 images in that directory. Most likely, the user cannot see all 100 on the screen — they would have to scroll to do that. If the user does not scroll, you have loaded the unseen images for no reason.
Second, do not load images that you already loaded. If you "open the activity containing that list several times", and you are loading all of the images each time, that is a waste, as you loaded many of those images previously.
Overall, do not load images yourself. There are plenty of image-loading libraries that are available, such as Picasso and Glide. A decent image-loading library will have a configurable cache (addressing my second point) and know how to load images on-demand in the background, even from rows in a ListView or RecyclerView (addressing my first point).

Caching, Passing and Viewing bitmaps efficiently Android

I have a news feed, where news items contain images. Each news item (in the table view) obtains a image url from a server and downloads the images asynchronously / from cache.
I will use a bit of pseudocode / Java to explain my process in the simplest terms possible.
NewsItemAdapter
Map<String, Bitmap> imageCache = new HashMap<String, Bitmap>(); // init cache
String imurl = get image url from appropriate NewsObject;
Bitmap value = imageCache.get(imurl);
if (value != null) { // if bitmap is in cache
load bitmap into image view from cache;
add bitmap to NewsObject for accessing later;
}else {
execute Asynchronous bitmap download task;
}
Asynchronous bitmap download task (The reason for scaleDownBitmap() is because of OutOfMemory errors I get)
doinBackground(Void...params){
myBitmap = download bitmap from imurl;
imageCache.put(imurl, scaleDownBitmap(myBitmap)); // put bitmap into cache
return scaleDownBitmap(myBitmap);
}
onPostExecute(Bitmap result){
load result into image view;
}
MainActivity
setOnNewsItemClickListener{
intent = get intent to mainNewsScreen; //after you click on news item
intent.putExta("newsBitmap", bitmap from NewsObject); // set in NewsItemAdapter
startActivity(intent);
}
MainNewsScreen
onCreate(){
load bitmap from intent extras into image view;
}
My main problem is if I remove the scaleDownBitmap() method found here I get OutOfMemory errors.
But I am losing a lot of image quality. As you can see I haven't used bitmap.recycle() at all, I'm not entirely sure where I'd use it as I need to keep the images in memory (I would have thought).
Any idea how to make this more efficient. I'm sure this would be helpful to a lot of people attempting to create a similar app.
Consider using an lrucache instead and storing the image on disk when it falls out of the cache.
Best practice is explained here:
http://developer.android.com/intl/es/training/displaying-bitmaps/cache-bitmap.html
Consider this code as an idea, some of it copied from the above link:
...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
#Override
protected void entryRemoved (boolean evicted, K key, V oldValue, V newValue) {
// Save your entry to disc instead
}
};
Google has made a DiscLruCache that you can simply download and use in your project (its usage is described in the above link):
https://developer.android.com/intl/es/samples/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.html
Also don't keep an infinite amount of news / images in the view. You're going to have to remove news items as the user scrolls through them and reads them.
Consider using a Recycler view for this (along with Cards if you want a Material design feel to your app):
https://developer.android.com/intl/es/reference/android/support/v7/widget/RecyclerView.html
<!-- A RecyclerView with some commonly used attributes -->
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager)
Cards:
https://developer.android.com/intl/es/reference/android/support/v7/widget/CardView.html
<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="#+id/info_text"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.CardView>
More information about combining a Recycler view with Card views:
https://developer.android.com/training/material/lists-cards.html

HashMap De-Serialization

I have a server/client app where I retrieve data from the server via Hessian/hessdroid. The data is very complex with HashMaps containing other HashMaps and images stored in byte arrays. I can display the data perfectly.
To not always query the server, I´m using a data structure as a cache. This data object I save to SD card using ObjectOutputStream when closing the app. When I restart it, I read it back to memory with an ObjectInputStream.
I´m having problems with the app only after reading the data from SD card. LogCat gives me the following output (100 times):
DEBUG/dalvikvm(4150): GetFieldID: unable to find field Ljava/util/HashMap;.loadFactor:F
and this in between the other messages:
INFO/dalvikvm-heap(4150): Grow heap (frag case) to 10.775MB for 281173-byte allocation
When the heap grows upon ~17 MB the app crashes.
I read several threads about HashMap Serialization and that there seems to be a bug when serializing between architectures, but for me the data transfer via Hessian works perfectly and I´m having the described problems only when reading the HashMaps from disk.
Any ideas?
The problem is not directly related to the HashMap Deserialization, as cyber-monk commented. There is indeed some kind of bug in Android or it´s HashMap implementation, but I don´t think it´s why the app crashes.
By now I solved the problem, using less images in the app. I had a gallery for example in which you could swipe from one image to the next in a flipper and loaded all the images at once. At a certain amount of images, there is not enough heap space.
My solution to this is, to not keep all the decoded images at once.
It´s done like this:
1) Hold the binary image data in memory (not a problem as long as the images are not that big)
2) Don´t load the binary image data into the ImageViews when creating the views of the flipper.
3) Set the binary image data into the ImageView that is displayed.
4) Keep the binary image data of the next and the last ImageView for better user experience)
5) "Unload" the ImageViews that are not displayed by setting its resource to the transparent color.
Here´s some code:
// initialize the viewFlipper by creating blank views
for (ComponentImageDto listElement : images) {
LinearLayout view = renderView();
flipper.addView(view);
}
showImage(flipper.getCurrentView());
renderView() just returns a LinearLayout containing an ImageView
Then I wrote some methods to show the next/previous image in which I set the binary data to the ImageView:
private void showNextElement() {
// show next flipper view
flipper.showNext();
// get current view
int displayedChild = flipper.getDisplayedChild();
View currentView = flipper.getCurrentView();
// load the binary data
showImage(currentView);
// get the next to last view index (if keeping max. 3 images at a time in memory)
int otherChild = (displayedChild - 2);
if (otherChild < 0) {
otherChild = otherChild + flipper.getChildCount();
}
// .. and remove it
removeImage(flipper.getChildAt(otherChild));
}
private void showPreviousElement() {
flipper.showPrevious();
int displayedChild = flipper.getDisplayedChild();
View currentView = flipper.getCurrentView();
showImage(currentView);
setTitle((CharSequence) currentView.getTag());
int otherChild = (displayedChild + 2) % flipper.getChildCount();
removeImage(flipper.getChildAt(otherChild));
}
private void removeImage(View view) {
ImageView imageView = (ImageView) view.findViewById(R.id.gallery_image);
if (imageView != null) {
imageView.setImageResource(R.color.transparent);
System.gc();
}
}
private void showImage(View view) {
ImageView imageView = (ImageView) view.findViewById(R.id.gallery_image);
if (imageView != null) {
bm = BitmapHelper.decodeByteArray(images.get(flipper.getDisplayedChild()).getImage().getBinaryObject());
imageView.setImageBitmap(bm);
}
}
To furtherly improve memory handling I´m using some code in BitmapHelper class that I found on stackoverflow, that helps to save memory for images by reducing their size.

Categories

Resources