Reputation: 509
Please did someone add lots of imageviews in a viewpager before? I have an activity that calls the fragment class to create fragments containing images into a viewpager and this fragment class contains methods that caches the image if it doesn't already exist in cache memory and resize the image and let the drawable be executed in an asynctask to decrease the time consumption of the front task... but with all these methods to eliminate the out of memory error I am still having this error!!! been two or three days now trying different methods and none of it worked... so any thoughts please!!
My code is:
import java.lang.ref.WeakReference;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class ScreenSlidePageFragment extends Fragment {
/**
* The argument key for the page number this fragment represents.
*/
public static final String ARG_PAGE = "page";
private LruCache<String, Bitmap> mMemoryCache;
/**
* The fragment's page number, which is set to the argument value for {@link #ARG_PAGE}.
*/
ImageView img;
int height;
int width;
private int mPageNumber;
private int[] pics = {R.drawable.intro1, R.drawable.intro2,R.drawable.intro3,R.drawable.intro4,R.drawable.intro5,R.drawable.intro6,R.drawable.intro7,R.drawable.intro8,
R.drawable.intro9,R.drawable.intro10,R.drawable.intro11,R.drawable.intro12,R.drawable.intro13,R.drawable.intro14,R.drawable.intro15,R.drawable.intro16,R.drawable.intro17,R.drawable.intro18,
R.drawable.intro19,R.drawable.intro20,R.drawable.intro21,R.drawable.intro22,R.drawable.intro23,R.drawable.intro24,R.drawable.intro25,R.drawable.intro26,R.drawable.intro27,R.drawable.intro28,R.drawable.intro29,R.drawable.intro30,
R.drawable.intro31,R.drawable.intro32,R.drawable.intro33,R.drawable.intro34,R.drawable.intro35,R.drawable.intro36,R.drawable.intro37,R.drawable.intro38,R.drawable.intro39,R.drawable.intro40,R.drawable.intro41,R.drawable.intro42,
R.drawable.intro43,R.drawable.intro44,R.drawable.intro45,R.drawable.intro46,R.drawable.intro47,R.drawable.intro48,R.drawable.intro49,R.drawable.intro50,R.drawable.intro51,R.drawable.intro52,R.drawable.intro53,R.drawable.intro54,
R.drawable.intro55,R.drawable.intro56,R.drawable.intro57,R.drawable.intro58,R.drawable.intro59,R.drawable.intro60,R.drawable.intro61,R.drawable.intro62,R.drawable.intro63,R.drawable.intro64,R.drawable.intro65,R.drawable.intro66,
R.drawable.intro67,R.drawable.intro68,R.drawable.intro69,R.drawable.intro70,R.drawable.intro71,R.drawable.intro72,R.drawable.intro73};
int count=72;//it's the number of the images-1;
/**
* Factory method for this fragment class. Constructs a new fragment for the given page number.
*/
public static ScreenSlidePageFragment create(int pageNumber) {
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
fragment.setArguments(args);
return fragment;
}
public ScreenSlidePageFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
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.getRowBytes()*bitmap.getHeight() / 1024;
}
};
mPageNumber = getArguments().getInt(ARG_PAGE);
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.image_view_layout, container, false);
img=(ImageView) rootView.findViewById(R.id.img_pager);
final String imageKey = String.valueOf(pics[count-mPageNumber]);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
img.setImageBitmap(bitmap);
} else {
img.setImageResource(pics[count-mPageNumber]);
BitmapWorkerTask task = new BitmapWorkerTask(img);
task.execute(pics[count-mPageNumber]);
}
return rootView;
}
@Override
public void onDetach(){
super.onDetach();
super.onDetach();
Bitmap bitmap = ((BitmapDrawable)img.getDrawable()).getBitmap();
bitmap.recycle();
//FragmentManager fragmentManager = getFragmentManager();
//FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//fragmentTransaction.remove(this).commit();
}
/**
* Returns the page number represented by this fragment object.
*/
public int getPageNumber() {
return mPageNumber;
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), params[0],width,height);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
}
and the activity is:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.Menu;
public class Introduction_Activity extends FragmentActivity {
private static final int NUM_PAGES = 73;
private ViewPager mPager;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private PagerAdapter mPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_introduction);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.imgs_viewpager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(NUM_PAGES-1,false);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.introduction_, menu);
return true;
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return ScreenSlidePageFragment.create(position);
}
@Override
public int getCount() {
return NUM_PAGES;
}
}
}
problem:
10-02 06:21:48.230: E/dalvikvm-heap(8944): Out of memory on a 7071376-byte allocation.
10-02 06:21:48.245: E/AndroidRuntime(8944): FATAL EXCEPTION: AsyncTask #4
10-02 06:21:48.245: E/AndroidRuntime(8944): java.lang.RuntimeException: An error occured while executing doInBackground()
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.os.AsyncTask$3.done(AsyncTask.java:278)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.lang.Thread.run(Thread.java:856)
10-02 06:21:48.245: E/AndroidRuntime(8944): Caused by: java.lang.OutOfMemoryError
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.Bitmap.nativeCreate(Native Method)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:618)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:593)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:445)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:468)
10-02 06:21:48.245: E/AndroidRuntime(8944): at packagename.ScreenSlidePageFragment.decodeSampledBitmapFromResource(ScreenSlidePageFragment.java:178)
10-02 06:21:48.245: E/AndroidRuntime(8944): at packagename.ScreenSlidePageFragment$BitmapWorkerTask.doInBackground(ScreenSlidePageFragment.java:148)
10-02 06:21:48.245: E/AndroidRuntime(8944): at packagename.ScreenSlidePageFragment$BitmapWorkerTask.doInBackground(ScreenSlidePageFragment.java:1)
10-02 06:21:48.245: E/AndroidRuntime(8944): at android.os.AsyncTask$2.call(AsyncTask.java:264)
10-02 06:21:48.245: E/AndroidRuntime(8944): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
10-02 06:21:48.245: E/AndroidRuntime(8944): ... 5 more
Upvotes: 3
Views: 5961
Reputation: 1294
step 1: in manifest file
android:largeHeap="true"
step 2
viewpager.offscreenPageLimit = 2 //kotlin
viewpaget.setOffscreenPageLimit(2) //java
and take a look at here for analyzing ur app memory usage
https://developer.android.com/studio/profile/android-profiler
Upvotes: 0
Reputation: 1172
For loading images use Glide Image library https://developer.android.com/topic/performance/graphics/index.html
in fragment onDestory method make every views and objects are null;
Upvotes: 0
Reputation: 7299
The lesson that I learnt the hard way is, "DO NOT REINVENT THE WHEEL". Use 3rd-party libraries like Picasso, Fresco, Glide or the like to get out of the situation like this. And additional bonanza is that you're code is much maintainable.
Upvotes: 0
Reputation: 3381
I faced same problem and solved it, as follows
Best example is given in android developer guide, follow this guide step by step.
implement inJustDecodeBounds http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Follow the below guide for asynchronous and caching, you can also download the sample from same link. http://developer.android.com/training/displaying-bitmaps/display-bitmap.html
Upvotes: 2
Reputation: 4233
android:largeHeap="true"
This is not a good solution. You are using largeHeap but this tag added from Android 3.0 and size of the large heap is double of original heap. That simply means it could be so many numbers depend on device manufacturer.
You should come up with a solution which take care of out of memory error for all type of devices. Solution like this will work on some device but not a guarantee it will work every where. What i am trying to say you might end having a large heap which still smaller then your requirement.
LruCache
is not known what you just described. by documentation,
A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.
It hold strong reference.
So do the following,
Upvotes: 4
Reputation: 5152
aquire large heap using this in manifest:
android:largeHeap="true"
and almost all problem solutions are present on these links related to memory out of error:
Memory Leak and Out of memory Error using List,LinkedList and HashMap
Memory Leak and Out of memory Error using LRU Cache
Memory Leak and Out of memory Error using Disk LRU Cache
Upvotes: 0
Reputation: 4382
Try this
add following into application tag in your manifest
android:largeHeap="true"
Hope it will help you.
Upvotes: 8