I am trying to load some very small images (average size is 90kb) into a gridview in Android. Whenever I load more than 9 images then I am getting memory issues. I have tried scaling the images to a smaller size and although this works to a certain extent it is not really a true solution as the picture quality is awful.
The code is below
private Context mContext;
private ArrayList<Bitmap> photos = new ArrayList<Bitmap>();
public Bitmap [] mThumbIds;
public ImageAdapter(Context c) {
mContext = c;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public Bitmap scaleBitmap(String imagePath) {
Bitmap resizedBitmap = null;
try {
int inWidth = 0;
int inHeight = 0;
InputStream in;
in = new FileInputStream(imagePath);
// decode image size (decode metadata only, not the whole image)
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
in.close();
in = null;
// save width and height
inWidth = options.outWidth;
inHeight = options.outHeight;
// decode full image pre-resized
in = new FileInputStream(imagePath);
options = new BitmapFactory.Options();
// calc rought re-size (this is no exact resize)
options.inSampleSize = Math.max(inWidth/300, inHeight/300);
// decode full image
Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);
// calc exact destination size
Matrix m = new Matrix();
RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
RectF outRect = new RectF(0, 0, 300, 300);
m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
float[] values = new float[9];
m.getValues(values);
// resize bitmap
resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return resizedBitmap;
}
public void populateGrid() {
File sdDir = new File("mnt/sdcard/Pictures");
File[] sdDirFiles = sdDir.listFiles();
for(File singleFile : sdDirFiles) {
String filePath = singleFile.getAbsolutePath();
Bitmap bmp = scaleBitmap(filePath);
photos.add(bmp);
}
mThumbIds = photos.toArray(new Bitmap[(photos.size())]);
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageBitmap(mThumbIds[position]);
return imageView;
}
#Override
public int getCount() {
return mThumbIds.length;
}
}
Two considerations:
The 90kb compressed image size doesn't really matter. The memory use is dictated by the actual resolution of the bitmap -- in this case, 300*300*4bpp, so about 360k per bitmap.
Gingerbread has some flaws because of the combination of the fact that Bitmap memory is stored in a native array (rather than on the Java heap) combined with the fact that garbage collection occurs concurrently. Because of this fact, it sometimes takes the memory manager longer to realize that Bitmap memory can be re-used.
So, the ramifications of this are:
Consider the actual decompressed Bitmap size when estimating memory usage.
Recycle intermediate bitmaps as much as possible, to help get the memory reclaimed faster. For example, if you are scaling a bitmap, save a reference to the pre-scaled source, and recycle the source after scaling is complete (compare to the result of the scaling, since it's possible that the same Bitmap is returned).
If you can, test your code on an ICS device. You can then use the heap inspection tools to get a sense about where the most memory is being used. Sine ICS allocates bitmap memory on the Java heap, you'll have an accurate picture of the bitmap memory usage.
Bitmap use a lot of memory, if you have the image saved into memory already it would probably be better to just use the path of the file and push it on an ImaveView.
ImageView img = new ImageView(getApplicationContext());;
img.setImageBitmap(BitmapFactory.decodeFile(media.getThumbPath()));
img.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
myLinearLayout.addView(img);
Something like this might work better. this way you are not storing all the Bitmaps into your heap.
Related
i'm developer an app that have more image (about 3000) that has space in filename like:
Im An image.jpg
being new to Android, I only found out now that are not accepted by the resources that have blank spaces or capital letters in the name, and this is a very big problem!
I use the same images in an application for iOS (without problems), and it would be almost impossible now to change all the names.
there is a way to solve this problem without renaming images?
if not, is there a way to quickly rename 3000 images?
thanks at all
there is a way to solve this problem without renaming images?
Nope. Res files can not have spaces.
You could put them in the assets folder and use them fom there, but you will have to get each image as a bitmap without the possibility of using the R.drawable.image_name notation.
I use this method:
public static Bitmap getFoodImage(Context context, String foodType){
InputStream bitmap = null;
Bitmap bit = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
try {
bitmap = context.getAssets().open(foodType.toUpperCase() + ".png");
if(bitmap != null){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int sWidth = metrics.widthPixels;
options.inJustDecodeBounds = true;
Bitmap dBit = BitmapFactory.decodeStream(bitmap, null, options);
int intrinsicHeight = options.outHeight;
int intrinsicWidth = options.outWidth;
//int height = (int) (intrinsicWidth * proportion);
int nHeight = (int) (intrinsicHeight/1.3);
try {
bitmap.reset();
} catch (IOException e) {
Log.e("SGNAM", e.getMessage());
}
options.inJustDecodeBounds = false;
dBit = BitmapFactory.decodeStream(bitmap, null, options);
Bitmap bP = Bitmap.createBitmap(dBit, 0, 0, intrinsicWidth, nHeight);
if(sWidth < intrinsicWidth){
bit = Bitmap.createScaledBitmap(bP, intrinsicWidth, intrinsicHeight, false);
}
else{
bit = Bitmap.createScaledBitmap(bP, sWidth, intrinsicHeight, false);
}
bitmap.close();
dBit = null;
bP = null;
}
} catch (IOException e) {
e.printStackTrace();
}
return bit;
}
As you can see i have all the image names in uppercase but you can skip that part if not needed (same for the png).
If you are in your onCreate method and the image you want is called An image.png (remember that in this example the .png is added by the method):
Context context = this;
Bitmap myBitmap = getFoodImage(context, An image);
You can set this bitmap as a resource for an imageView this way:
ImageView myImageView = findViewById(R.id.imageView);
myImageView.setImageBitmap(myBitmap);
This should do he trick.
My app is an OCR app base on Tesseract. It will do OCR task from camera picture. Users can take many pictures and put them into an OCR queue. To get more accuracy, I want to keep high quality image (I choose min size is 1024 x 768 (maybe larger in future), JPEG, 100% quality). When users take many pictures, there are three things to do:
Save the image data byte[] to file and correct EXIF.
Correct the image orientation base on device's orientation. I know there are some answers that said the image which comes out of the camera is not oriented automatically, have to correct it from file, like here and here. I'm not sure about it, I can setup the camera preview orientation correctly, but the image results aren't correct.
Load bitmap from taken picture, convert it to grayscale and save to another file for OCR task.
And here is my try:
public static boolean saveBitmap(byte[] bitmapData, int orientation, String imagePath, String grayScalePath) throws Exception {
Boolean rotationSuccess = false;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap originalBm = null;
Bitmap bitmapRotate = null;
Bitmap grayScale = null;
FileOutputStream outStream = null;
try {
// save directly from byte[] to file
saveBitmap(bitmapData, imagePath);
// down sample
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int sampleSize = calculateInSampleSize(options, Config.CONFIG_IMAGE_WIDTH, Config.CONFIG_IMAGE_HEIGHT);
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
originalBm = BitmapFactory.decodeFile(imagePath, options);
Matrix mat = new Matrix();
mat.postRotate(orientation);
bitmapRotate = Bitmap.createBitmap(originalBm, 0, 0, originalBm.getWidth(), originalBm.getHeight(), mat, true);
originalBm.recycle();
originalBm = null;
outStream = new FileOutputStream(new File(imagePath));
bitmapRotate.compress(CompressFormat.JPEG, 100, outStream);
// convert to gray scale
grayScale = UIUtil.convertToGrayscale(bitmapRotate);
saveBitmap(grayScale, grayScalePath);
grayScale.recycle();
grayScale = null;
bitmapRotate.recycle();
bitmapRotate = null;
rotationSuccess = true;
} catch (OutOfMemoryError e) {
e.printStackTrace();
System.gc();
} finally {
if (originalBm != null) {
originalBm.recycle();
originalBm = null;
}
if (bitmapRotate != null) {
bitmapRotate.recycle();
bitmapRotate = null;
}
if (grayScale != null) {
grayScale.recycle();
grayScale = null;
}
if (outStream != null) {
try {
outStream.close();
} catch (IOException e) {
}
outStream = null;
}
}
Log.d(TAG,"save completed");
return rotationSuccess;
}
Save to file directly from byte[]
public static void saveBitmap(byte[] bitmapData, String fileName) throws Exception {
File file = new File(fileName);
FileOutputStream fos;
BufferedOutputStream bos = null;
try {
final int bufferSize = 1024 * 4;
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos, bufferSize);
bos.write(bitmapData);
bos.flush();
} catch (Exception ex) {
throw ex;
} finally {
if (bos != null) {
bos.close();
}
}
}
Calculate scale size
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
When save complete, this image is loaded into thumbnail image view by UIL. The problem is the save task is very slow (wait some second before save complete and load into view), and sometime I got OutOfMemory exception. Is there any ideas to reduce the save task and avoid OutOfMemory exception?
Any help would be appreciated!
P/S: the first time I try to convert byte[] to bitmap instead of save to file, and then rotate and convert to grayscale, but I still got above issues.
Update: here is the grayscale bitmap process:
public static Bitmap convertToGrayscale(Bitmap bmpOriginal) {
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return bmpGrayscale;
}
The OutOfMemory exception seldom occurred (just a few times) and I can't reproduce it now.
Update:
Since you're still saying that the method takes too long time I would define a callback interface
interface BitmapCallback {
onBitmapSaveComplete(Bitmap bitmap, int orientation);
onBitmapRotateAndBWComlete(Bitmap bitmap);
}
Let your activity implement the above interface and convert the byte[] to bitmap in top of your saveBitmap method and fire the callback, before the first call to save. Rotate the imageView based on the orientation parameter and set a black/white filter on the imageView to fool the user into thinking that the bitmap is black and white (do this in your activity). See to that the calls are done on main thread (the calls to imageView). Keep your old method as you have it. (all steps need to be done anyway) Something like:
public static boolean saveBitmap(byte[] bitmapData, int orientation, String imagePath, String grayScalePath, BitmapCallback callback) throws Exception {
Boolean rotationSuccess = false;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap originalBm = null;
Bitmap bitmapRotate = null;
Bitmap grayScale = null;
FileOutputStream outStream = null;
try {
// TODO: convert byte to Bitmap, see to that the image is not larger than your wanted size (1024z768)
callback.onBitmapSaveComplete(bitmap, orientation);
// save directly from byte[] to file
saveBitmap(bitmapData, imagePath);
.
.
// same as old
.
.
saveBitmap(grayScale, grayScalePath);
// conversion done callback with the real fixed bitmap
callback.onBitmapRotateAndBWComlete(grayScale);
grayScale.recycle();
grayScale = null;
bitmapRotate.recycle();
bitmapRotate = null;
rotationSuccess = true;
How do you setup your camera? What might be causing the long execution time in the first saveBitmap call, could be that you are using the default camera picture size settings and not reading the supported camera picture size and choosing best fit for your 1024x768 image needs. You might be taking big mpixel images and saving such, but in the end need you need < 1 mpixles (1024x768). Something like this in code:
Camera camera = Camera.open();
Parameters params = camera.getParameters();
List sizes = params.getSupportedPictureSizes();
// Loop camera sizes and find best match, larger than 1024x768
This is probably where you will save most of the time if you are not doing this already. And do it only once, during some initialization phase.
Increase the buffer to 8k in saveBitmap, change the 1024*4 to 1024*8, this would increase the performance at least, not save any significant time perhaps.
To save/reuse bitmap memory consider using inBitmap field, if you have a post honeycomb version, of BitmapFactory.Options and set that field to point to bitmapRotate bitmap and send options down to your convertToGrayscale method to not need allocating yet another bitmap down in that method. Read about inBitmap here: inBitmap
I have a problem that I have 80 to 150 images of very High Resolution and I have to show them in a view pager. I had written the simple mechanism of decoding but get OutofMemory Error after the 2nd Page. I tried very much but unable to find the exact solution to avoid that. My own suggestion is that if we will able to load a image in parts on an Image View then perhaps we avoid this but I don't know how to achieve this. Please suggest any solution regarding this.
Code:
private class MyPagerAdapter extends PagerAdapter {
#Override
public int getCount() {
return Integer.parseInt(pagesCount);
}
#Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0==((ImageView)arg1);
}
#Override
public void destroyItem(View container, int position, Object object) {
View view = (View)object;
((ViewPager) container).removeView(view);
view = null;
}
#Override
public View instantiateItem(View container, final int position) {
final ImageView imgPage = new ImageView(getApplicationContext());
/*imgPage.setImageURI(Uri.withAppendedPath(
Uri.parse(Environment.getExternalStorageDirectory()+"/MAGZ/"+magName+issueName), "" + (issueName+position)+".png"));*/
//imgPage.setImageBitmap(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/MAGZ/"+magName+issueName+"/"+(issueName+position)));
//destroyItem(container, position, imgPage);
imgPage.setScaleType(android.widget.ImageView.ScaleType.FIT_XY);
imgPage.setImageDrawable(getImageFromSdCard(issueName+position));
//imgPage.setImageResource(getImageFromSdCard(issueName+position));
((ViewPager) container).addView(imgPage,0);
return imgPage;
}
}
public Drawable getImageFromSdCard(String imageName) {
Drawable d = null;
try {
String path = Environment.getExternalStorageDirectory()+"/MAGZ/"+magName+"/"+magName+issueName;
bitmap = BitmapFactory.decodeFile(path + "/" + imageName
+ ".png");
// d = new BitmapDrawable(bitmap);
d = new BitmapDrawable(decodeFile(new File(path+"/"+imageName+".png")));
// bitmap.recycle();
bitmap = null;
} catch (IllegalArgumentException e) {
bitmap = null;
}
return d;
}
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_SIZE=550;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
Thanks in advance.
inJustDecodeBounds & inSampleSize
You are decoding high resolution images at each PagerAdapter call, and if you are willing to reduce the decoded bitmap memory by powers of 2 (x/2, x/4 ... x : original bitmap size), then go for this method
Bitmap.recycle()
A very handy method, when used at the right place, can work wonders. I am assuming that you are setting a BitmapDrawable or calling setImageBitmap to an ImageView. Add the following snippet in destroyItem() callback of PagerAdapter.
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
ImageView imageView = (ImageView) view.findViewById(R.id.image_view);
Drawable drawable = imageView.getDrawable();
if(drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable != null) {
Bitmap bitmap = bitmapDrawable.getBitmap();
if(bitmap != null && !bitmap.isRecycled()) bitmap.recycle();
}
}
((ViewPager) container).removeView(view);
}
The idea is that when any child view is removed from Pager, its bitmap should get recycled, that way you would not depend on GC calls to clear memory for you.
largeHeap = true
Add this property in Manifest.xml <application> tag. Wherever largeHeap is supported, it will increase heap memory to some large value, even goes till 8 times.
Also, in general don't hold any silly references to any bitmap, just decode them and assign it to View, rest will be taken care of.
Hope that helps. :)
I'll suggest you to look at official android developers sample code called BitmapFun and use it for your purpose. Actually you missed o2.inPurgable=true; Also there is no need to use o and o2, o is good enough.
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_SIZE=550;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;
//Decode with inSampleSize
//BitmapFactory.Options o2 = new BitmapFactory.Options();
o.inSampleSize=scale;
o.inJustDecodeBounds = false;
o.inPurgealbe = true;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o);
} catch (FileNotFoundException e) {}
return null;
}
Remove the line bitmap = BitmapFactory.decodeFile(path + "/" + imageName
+ ".png"); from your Drawable getImageFromSdCard(String imageName) method to begin with. Also remove the bitmap variable in that method since it´s unused, now you have removed unnecessary allocations, the problem you experience could be that the GC simply does not cope with the amount of garbage / allocations that was made since you did twice as many that was needed. The method should look something like:
public Drawable getImageFromSdCard(String imageName) {
Drawable d = null;
String path = Environment.getExternalStorageDirectory()+"/MAGZ/"+magName+"/"+magName+issueName;
d = new BitmapDrawable(decodeFile(new File(path+"/"+imageName+".png")));
return d;
}
Also reuse your o variable in decodeFile method just remember to flip the boolean inJustDecodeBounds after the first call.
Also do not catch runtime exceptions, bad practice, they're there because of just runtime errors. You´re hiding the real problem by catching them.
BitmapDrawable(Bitmap bitmap) constructor has been deprecated since api version 4, checkout BitmapDrawable for what constructor you should be using.
I've got very critical problem.
Only Android 4.1, Bitmap is recycled automatically!
I didn't call recycle() in my code!
My project works fine in other OS versions( ~ 4.0.3) with any resolutions.
Other projects have same problem, too.
All image files are in drawable-nodpi folder.
I resized them to fit for resolution of any devices, always.
public Bitmap GetBitmap(int resource){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither = true;
options.inPurgeable = true;
Bitmap tmp = null;
try{
tmp = BitmapFactory.decodeResource(mResources, resource, options);
}catch(OutOfMemoryError e){
options.inSampleSize = 2;
tmp = BitmapFactory.decodeResource(mResources, resource, options);
}
return tmp;
}
public Bitmap GetScaledBitmap(int resource, int width, int height, boolean filter){
Bitmap tmp = GetBitmap(resource);
Bitmap img = Bitmap.createScaledBitmap(tmp, width, height, filter);
tmp.recycle();
tmp = null;
return img;
}
In my testing,
Same bitmap instance, but the problem occurs depending on resizing value.
ex)
int width = 100;
Bitmap imgStar = MyResourceManager.getInstance().GetScaledBitmap(R.drawable.star, width, width , true); -> returns recycled instance.
width = 200;
imgStar = MyResourceManager.getInstance().GetScaledBitmap(R.drawable.star, width, width, true); -> returns normal instance.
In different resolutions, imgStar works fine, but the problem occurs in other bitmap instance.
Similarly, When I change resizing value, it works fine.
In same resolution, the problem occurs in other bitmap instance, if I change the name of image files folder.
drawable-nodpi -> drawable -> drawable-ldpi, ..., drawable-xdpi.
Same resizing value, it works fine if I put other resource id.
ex)
int width = 100;
Bitmap imgStar = MyResourceManager.getInstance().GetScaledBitmap(R.drawable.star, width, width , true); -> returns recycled instance.
imgStar = MyResourceManager.getInstance().GetScaledBitmap(R.drawable.diamond, width, width, true); -> returns normal instance.
Please... what can I do?! T ^ T
The reason you're getting different results from different sizes may be because createScaledBitmap will return the original object if it is the same size you are scaling to.
I had the same problem, doing the same thing you are. I was able to fix it this way:
public Bitmap GetScaledBitmap(int resource, int width, int height, boolean filter) {
Bitmap tmp = GetBitmap(resource);
Bitmap img = Bitmap.createScaledBitmap(tmp, width, height, filter);
//copy the image to be sure you are not using the same object as the tmp bitmap
img=img.copy (Bitmap.Config.RGB_565,false);
tmp.recycle();
tmp = null;
return img;
}
Here I copied the bitmap to make sure it wasn't just a refernce to the tmp bitmap object before I recycled the tmp bitmap. Of course you can use any bitmap config you need.
I believe XdebugX was correct in his finding although you don't need to create a copy. Simply check if the memory locations are the same between your resized and original bitmaps.
public Bitmap GetScaledBitmap(int resource, int width, int height, boolean filter){
Bitmap tmp = GetBitmap(resource);
if (tmp == null) {
return null;
}
Bitmap img = Bitmap.createScaledBitmap(tmp, width, height, filter);
/***
* Bitmap#createScaledBitmap will return the original object
* if it is the same size you are scaling to.
*/
if (tmp != img) {
Log.d(TAG, "Full size image recycled");
tmp.recycle();
} else {
Log.w(TAG, "Resized bitmap was the same as the fullsize bitmap");
}
return img;
}
I'd rather suggest to try checking if the bitmap is already recycled before to recycle:
if (!tmp.isRecycled()) tmp.recycle();
I'm using a viewpager to load around 50 webviews... All the webviews are loaded into assests and each weview has an HTML page that is accessing around 70 images each... As i swipe, my app is crashing after around 30 pages, might be cos of the webviews still keep their reference to the images in the assests folder... Is there any way to release the webviews that the Viewpager is not using at that particular time?
awesomePager.setAdapter(new AwesomePagerAdapter(this, webviewdata));
Details:
Android WebView Memory Leak when loading html file from Assets
Failed adding to JNI local ref table (has 512 entries)
"Thread-375" prio=5 tid=15 RUNNABLE
while dynamically loading webview on viewpager
Logcat:
Try to scale down bitmap.Most of the time Bitmap is a main reason we get Memory issue.
Also learn about how to recycle the bitmaps.
Following snippet will help you.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile( filename, options );
options.inJustDecodeBounds = false;
options.inSampleSize = 2;
bitmap = BitmapFactory.decodeFile( filename, options );
if ( bitmap != null && exact ) {
bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
}
Also make sure you did override following method.
#Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((TextView) view);
}
Or you can create a function to Scale Down Bitmap
private byte[] resizeImage( byte[] input ) {
if ( input == null ) {
return null;
}
Bitmap bitmapOrg = BitmapFactory.decodeByteArray(input, 0, input.length);
if ( bitmapOrg == null ) {
return null;
}
int height = bitmapOrg.getHeight();
int width = bitmapOrg.getWidth();
int newHeight = 250;
float scaleHeight = ((float) newHeight) / height;
// creates matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleHeight, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
bitmapOrg.recycle();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
resizedBitmap.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
resizedBitmap.recycle();
return bos.toByteArray();
}
The WebView works with the JNI and this can only hold 512 local references, try loading your content directly from web and the problem shouldn't occur.
I get this problem when I override shouldInterceptRequest(WebView view, String url) in webviewclient and hand local references from my own caching mechanism.
This might be a bug in the webview itself. At least it isn't how it should behave if you ask me.
Setting layer type of webview to software works.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
But now you lose hardware acceleration and your webview may slow down.