UniversalImageLoader crash (while scrolling wild up and down) - java

I am having crashes in a gridview using NOSTRAs UniversalImageLoader. There are around 1.5k images in the list to scroll through. But i guess its not related the bitmaps themself in the memory, but its related to too many thread creations. Basically i am scrolling up and down and up and down very wild. And i guess there are too many thread created and i am seeing this in the log very often
D/dalvikvm: create interp thread : stack size=32KB
D/dalvikvm: create new thread
D/dalvikvm: new thread created
D/dalvikvm: update thread list
and eventually i see
E/dalvikvm: Thread creation failed (err=Invalid argument, stacksize=1085584KB)
A/libc: Fatal signal 7 (SIGBUS) at 0x84849142 (code=1), thread 28383 (nomad5.beatsnap)
or sometimes this
E/dalvikvm: Thread creation failed (err=Invalid argument, stacksize=1085584KB)
A/libc: Fatal signal 11 (SIGSEGV) at 0x84849142 (code=2), thread 14546 (nomad5.beatsnap)
sometimes is paired with
java.lang.OutOfMemoryError: pthread_create (stack size 16384 bytes) failed: Try again
but sometimes this crash is not shown in the log. Here is my UIL config
.denyCacheImageMultipleSizesInMemory().diskCacheSize(64 * 1024 * 1024).threadPoolSize(5);
and here my display options
.bitmapConfig(Bitmap.Config.RGB_565)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
.cacheInMemory(false).cacheOnDisk(true).showImageOnFail(R.drawable.ic_image_choose_error)
.showImageForEmptyUri(R.drawable.ic_image_choose_error)
I am loading images already in a very small size. Here is the loading call
ImageLoader.getInstance()
.displayImage("file://" + this.imageList.get(position),
new ImageViewAware(imageView),
this.options,
new ImageSize(100, 100),
new SimpleImageLoadingListener()
{
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage)
{
...
}
},
null);
By the way, my apps memory is constantly stable at around 40MB.
So as you see i disabled all the memory caching. I am also using large heap in the XML.
Any suggestions how to avoid this crashes? Is there a way to limit the thread creation counts?
Update #1
I have tried it with loadImage and removed all other code. Its still happening.
Here is my adapter code. The imageList is a list with ~1.5k entries. Just paths to the images on the phone.
public class ImageAdapter extends BaseAdapter
{
// all the paths
private final ArrayList<String> imageList;
// the layout inflater
private final LayoutInflater layoutInflater;
// checked array
private final SparseBooleanArray checkedArray = new SparseBooleanArray();
// options for loading into the grid
private final DisplayImageOptions options;
/**
* Constructor to use
*/
public ImageAdapter(#NonNull Context context,
#NonNull ArrayList<String> imageList)
{
// init members
this.layoutInflater = LayoutInflater.from(context);
this.imageList = imageList;
// image options
this.options = new DisplayImageOptions.Builder().bitmapConfig(Bitmap.Config.RGB_565)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
.cacheInMemory(false)
.cacheOnDisk(true)
.showImageOnFail(R.drawable.ic_image_choose_error)
.showImageForEmptyUri(R.drawable.ic_image_choose_error)
.imageScaleType(ImageScaleType.EXACTLY)
.build();
}
#Override
public int getCount()
{
return this.imageList.size();
}
#Override
public Object getItem(int position)
{
return null;
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(convertView == null)
{
convertView = this.layoutInflater.inflate(R.layout.fragment_image_choose_element, null);
}
convertView.setTag(position);
// load the image
final View finalConvertView = convertView;
ImageLoader.getInstance()
.loadImage("file://" + this.imageList.get(position),
new ImageSize(100, 100),
this.options,
null);
return convertView;
}
}
and here is the file fragment_image_choose_element.xml that is inflated in the getView method:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id = "#+id/image_choose_element"
xmlns:android = "http://schemas.android.com/apk/res/android"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_gravity = "center"
android:gravity = "center">
<ImageView
android:id = "#+id/image_choose_element_image"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:adjustViewBounds = "false"
android:scaleType = "centerCrop"/>
</RelativeLayout>

Related

Android RecyclerView : Memory usage increases during scrolling if 'setIsRecyclabe(false)' is not used

In my android application(Java) I am displaying a list of around 1800 contacts in a recyclerview. While doing a memory profile it was found that when scrolling the recycler view the memory usage was increasing rapidly. So I found this question here which was mentioning the same problem and tried out the solution which was to setIsRecyclable(false) in onBindViewHolder and it worked. The profiling results are given below.
Case 1 : setIsRecyclable(False) not used
Initial memory usage : ~ 40M
[ Java=5.9M Native=5M Graphics=20.3M Stack=0.3M Code=5.3M Others =0.8M ]
Peak memory usage : ~ 345M
[ Java=187.5M Native=39.1M Graphics=101.5M Stack=0.4M Code=11.6M Others =6.5M ]
Also the peak memory usage was found to increase with increase in number of items in the list. After the continuous scrolling is stopped for a while the memory usage does come down but only to around 162 MB.
Case 2 : after adding setIsRecyclable(False) to onBindViewHolder
Initial memory usage : ~ 42M
[ Java=5.8M Native=5.5M Graphics=20.2M Stack=0.3M Code=9.4M Others =0.8M ]
Peak memory usage : ~ 100M
[ Java=43.9M Native=9.7M Graphics=32.6M Stack=0.4M Code=11.7M Others =2.2M ]
Also, in this case, memory usage was not affected significantly by increasing number of items in list. Although peak memory usage is about 100MB the average stays at around 70 MB for most of the time which is even better.
Source Code of fragment containing recyclerView
Note :
* Adapter class is defined as an inner class of Fragment class and ViewHolder class is defined as an inner class of Adapter class.
* 'App.personList' is a static arrayList holding the list of contacts and App is the ViewModel class.
* adapter1 is the only adapter of interest. Please avoid adapter2(handles another small list)
public class FragmentAllContacts extends Fragment
{
public static MainActivity main;
public RecyclerView contactsView, tagsView;
LinearLayoutManager llm, lln;
Button filterCloseButton;
CardView filterView;
Adapter_ContactListView adapter1;
Adapter_TagListView adapter2;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
main = (MainActivity) getActivity();
return inflater.inflate(R.layout.fragment_all_contacts, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
adapter1 = new Adapter_ContactListView(App.personList,getContext());
adapter2 = new Adapter_TagListView(App.tagList,getContext());
filterView = getView().findViewById(R.id.cardView7);
FloatingActionButton fab = getView().findViewById(R.id.create_contact_fab);
contactsView = getView().findViewById(R.id.allContacts_recyclerView);
contactsView.setAdapter(adapter1);
llm = new LinearLayoutManager(main.getBaseContext());
contactsView.setLayoutManager(llm);
contactsView.scrollToPosition(App.AllConnections.scrollPosition);
tagsView = getView().findViewById(R.id.allTags_recyclerView);
tagsView.setAdapter(adapter2);
lln = new LinearLayoutManager(main.getBaseContext(), LinearLayoutManager.HORIZONTAL, false);
tagsView.setLayoutManager(lln);
}
class Adapter_ContactListView extends RecyclerView.Adapter<Adapter_ContactListView.ViewHolder> implements Filterable {
List<Person_PersistentData> contactsFiltered;
Context context;
public Adapter_ContactListView(List<Person_PersistentData> list, Context context)
{
this.contactsFiltered = list;
this.context = context;
}
#Override
public Adapter_ContactListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_list, parent, false);
Adapter_ContactListView.ViewHolder pane = new Adapter_ContactListView.ViewHolder(v);
return pane;
}
#Override
public void onBindViewHolder(Adapter_ContactListView.ViewHolder pane, int position)
{
pane.setIsRecyclable(false);
final Person_PersistentData rec = contactsFiltered.get(position);
pane.nameView.setText(rec.personName + " (" + rec.personID + ")");
Uri imageUri = App.FSManager.getProfilePic(rec.personID);
if (imageUri != null) {pane.imageView.setImageURI(imageUri);}
else {pane.imageView.setImageResource(R.drawable.ico_60px);}
if (App.AllConnections.personSelectionStack.contains(rec.personID)) {pane.cv.setBackgroundColor(context.getResources().getColor(R.color.rgb_000_070_100));}
else
{pane.cv.setBackgroundColor(context.getResources().getColor(R.color.rgb_020_020_020));}
pane.cv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
if(App.AllConnections.selectionMode)
{
App.Person_SelectionInput(rec.personID);
Adapter_ContactListView.this.notifyDataSetChanged();
}
else
{
App.PersonInfo.id = rec.personID;
main.startTask(T.personInfo);
}
}
});
pane.cv.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view)
{
App.Person_SelectionInput(rec.personID);
Adapter_ContactListView.this.notifyDataSetChanged();
return false;
}
});
//animate(holder);
}
#Override
public int getItemCount() {
//returns the number of elements the RecyclerView will display
return contactsFiltered.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public Filter getFilter() { ... }
#Override
public long getItemId(int position)
{
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
class ViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView nameView;
ImageView imageView;
public ViewHolder(#NonNull View itemView)
{
super(itemView);
cv = itemView.findViewById(R.id.cardView);
nameView = itemView.findViewById(R.id.name);
imageView = itemView.findViewById(R.id.imageViewZ);
}
}
}
}
Question
So 'setIsRecyclable(False)' is supposed to prevent recycling of views and this should be causing more memory usage. But instead it is showing the opposite behavior. Also i think the app will surely crash if it has to handle an even larger list without using setIsRecyclable(false). Why is this happening ?
getItemId(int) and getItemViewType(int) should NEVER return position itself, you're violating recyclerview contract by forcing it to create new viewholders for every single position intead of re-using existing views.
This is the cause of your issue - every position has unique itemViewType so they start to fill up recycledViewPool very rapidly since they're only being inserted and never being taken out of it. setIsRecyclable(False) circumvents the issue by not putting them in recyclerViewPool but that doesn't fix the problem of lack of view recycling.
Just delete getItemId and getItemViewType overrides because you're not using them properly.

Using RecyclerView.setAdapter from worker thread

Using memberRecList RecyclerView, I'm trying to list every member of a group horizontally, but I get this issue when I set its adapter:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
NOTE: I've included some notes in the comments, scroll to the bottom to see them.
Here is the RecycleView initialization:
// I do InitMemberList() in the OnCreate method of my activity
private void InitMemberList()
{
LinearLayoutManager layoutManager =
new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
memberRecList = (RecyclerView) findViewById(R.id.member_list);
memberRecList.setHasFixedSize(true);
memberRecList.setLayoutManager(layoutManager);
//NOTE 1 (scroll to bottom)
}
My dataset is the ArrayList members. I retrieve it from a worker thread using FirebaseDatabase like so:
//this is also in the OnCreate() method, doesn't matter if it's before
//or after InitMembersList() because it starts a separate thread.
//I'm showing it just in case I have any errors here
private void GetMemberInfo(final String userId)
{
//NOTE 2 (scroll to bottom)
//Getting the reference of the database
DatabaseReference ref = userDB.getReference().getRef();
Query query = ref.child(userId);
//Firebase's method names are confusing. These below just retrieve the
//data from the database ONCE.
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
tempMember = dataSnapshot.getValue(User.class); //Getting member info
members.add(tempMember); //adding member info to the list
memberCount --; //number of members left to retrieve
if(memberCount == 0) {
//NOTE 3 (scroll to bottom)
PopulateMemberList(); //the adapter is set inside here
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
I set members adapter in the PopulateMemberList() method like so:
private void PopulateMemberList()
{
MembersListAdapter adapter = new MembersListAdapter(members);
memberRecList.setAdapter(adapter);
}
Here is my custom MembersListAdapter:
public class MembersListAdapter extends RecyclerView.Adapter<MembersListAdapter.MemberViewHolder> {
private ArrayList<User> users = new ArrayList<>();
public MembersListAdapter(ArrayList<User> users) {
this.users = users;
}
public class MemberViewHolder extends RecyclerView.ViewHolder{
TextView rep_txt;
ImageView memberImageView;
public MemberViewHolder(View itemView) {
super(itemView);
rep_txt = (TextView) itemView.findViewById((R.id.member_rep_text));
memberImageView = (ImageView) itemView.findViewById(R.id.member_profile_image);
}
}
#Override
public int getItemCount() {
return users.size();
}
#Override
public MemberViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//NOTE 4 (scroll to bottom)
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.member_list_item, parent, true);
return new MemberViewHolder(itemView);
}
#Override
public void onBindViewHolder(MemberViewHolder holder, int position) {
final User currentMember = users.get(position);
//changing the text of my textviews
//Loading Member Image
Picasso.with(GroupActivity.this)
.load(currentMember.getProfile_picture_ref())
.into(holder.memberImage);
}
NOTES:
NOTE 1: I tried setting the memberRecList adapter as an empty adapter at first inside InitMemberList() and then setting the new adapter once I got the data from Firebase. Didn't work. Gave me the same error.
NOTE 2: I use the GetMemberInfo() inside a loop where I pass the userId one by one. Firebase gurus will tell me to instead make a foreach loop with dataSnapshot.getChildren() to retrieve users one by one and then filter them, but it's not feasible because of the size of my database. I'd be scanning through a million users just to retrieve 5 of them.
NOTE 3: Using memberCount to find if member list retrieval is finished allows me to start working with the data after it is fully retrieved. Without this, trying to access the data gives Nullpointerexception because most of the it can't be received in time.
NOTE 4: I'm assuming this is where my error lies? I tried getting the itemView parent and then removing **itemView* from it (exactly what the error message says I should do):
#Override
public MemberViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.member_list_item, parent, true);
ViewGroup mParent = (ViewGroup) itemView.getParent();
if(mParent!=null)
mParent.removeView(itemView);
return new MemberViewHolder(itemView);
}
but I get a million instances of the following error:
E/AndroidRuntime: at android.view.ViewGroup.resetResolvedLayoutDirection(ViewGroup.java:6688)
and then a StackOverflow error and the app crashes:
D/Error: ERR: exClass=java.lang.StackOverflowError
D/Error: ERR: exMsg=stack size 8MB
D/Error: ERR: file=ViewGroup.java
D/Error: ERR: class=android.view.ViewGroup
D/Error: ERR: method=resetResolvedLayoutDirection line=6687
D/Error: ERR: stack=java.lang.StackOverflowError: stack size 8MB
at android.view.ViewGroup.resetResolvedLayoutDirection(ViewGroup.java:6687)
Afterwards, the last line is repeated at least 100 times and at the end:
D/Error: ERR: TOTAL BYTES WRITTEN: 41276
EDIT: Forgot to mention, I know FirebaseRecyclerAdapter exists, but I can't use it. If you know of other libraries which could help me, please do tell. Also, would've preferred to use a ListView rather than a RecyclerView, but I need to list the items horizontally (so that I can scroll left/right) and I think that's impossible with ListView.
You get an IllegalStateException because of this:
#Override
public MemberViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//NOTE 4 (scroll to bottom)
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.member_list_item, parent, true);
return new MemberViewHolder(itemView);
}
When you inflate a view for a RecyclerView you shouldn't add it to its parent, android does it for you.
Change the flag to false in the following line to not add view to parent:
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.member_list_item, parent, false);

Picasso on custom listview

I'm a beginner with android and am trying to use the image from google places API to my custom listview. I am trying to do this using Picasso. I can get the text no problem but when I'm trying to attach the image from the url it gives me an "Target must not be null" error. Any comments/help/suggestions is appreciated.
My custom listrow places_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<ImageView
android:id="#+id/placeicon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.10"/>
<TextView
android:id="#+id/placeinfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="20dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/placeicon"
android:layout_toEndOf="#+id/placeicon" />
</RelativeLayout>
My Async PostExecute code:
ImageView places_icon = (ImageView) findViewById(R.id.placeicon);
venuesfound = (ArrayList) parsedataFound(result);
ArrayList<String> venuesList = new ArrayList<>();
for(int i = 0; i<venuesfound.size();i++){
if(venuesfound.get(i).getImageURL() != null){
Picasso.with(getApplicationContext())
.load(venuesfound.get(i).getImageURL())
.into(places_icon);
}
venuesList.add(venuesfound.get(i).getName()
+ "\nOpen: " + venuesfound.get(i).getOpenNow()
+ "\n(" + venuesfound.get(i).getCategory() + ")");
}
placesList = (ListView) findViewById(R.id.places_list);
placesAdapter = new ArrayAdapter(DisplayPlacesActivity.this, R.layout.places_layout, R.id.placeinfo, venuesList);
placesList.setAdapter(placesAdapter);
Logcat:
java.lang.IllegalArgumentException: Target must not be null.
at com.squareup.picasso.RequestCreator.into(RequestCreator.java:618)
at com.squareup.picasso.RequestCreator.into(RequestCreator.java:601)
at com.example.johnchy.samplegui.DisplayPlacesActivity$dataRequest.onPostExecute(DisplayPlacesActivity.java:102)
at com.example.johnchy.samplegui.DisplayPlacesActivity$dataRequest.onPostExecute(DisplayPlacesActivity.java:56)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5419)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
at dalvik.system.NativeStart.main(Native Method)
Again, any help is appreciated!
The problem is, you try to attach the image to an ImageView, that is not there yet. Since you want to show the image in your list, you need to move the attachment into the adapter, since this is where you create the new ImageView. You will need to create a new ArrayAdapter for this purpose:
public class PicassoAdapter extends ArrayAdapter<String> {
public PicassoAdapter(List<String> urls, Context context){
super(context, 0, urls);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
PlaceViewHolder holder;
//ListView tries to reuse invisible Row-Views to save memory, thats why this method can be called with null OR actually a ready View
if(convertView == null){
//in this case we need to create a new View
//create a holder Object that we will attach to the view
holder = new PlaceViewHolder();
//in this line we actually create a new row from the xml-file
convertView = View.inflate(getContext(), R.layout.places_layout, parent, false);
//we attach the Image- and Text-View to our holder, so we can access them in the next step
holder.placeIcon = (ImageView)convertView.findViewById(R.id.placeicon);
holder.placeInfo = (TextView)convertView.findViewById(R.id.placeinfo);
convertView.setTag(holder)
} else {
//if the view is already created, simply get the holder-object
holder = (PlaceViewHolder)convertView.getTag();
}
//I assume the URL is a String, if you need another Type, simply change it here and in class declaration
String url = getItem(position);
Picasso.with(getContext())
.load(url)
//now we have an ImageView, that we can use as target
.into(holder.placeIcon);
//you can set the info here
holder.placeInfo.setText("test");
}
}
//we need this class as holder object
public static class PlaceViewHolder {
private ImageView placeIcon;
private TextView palceInfo;
}
}
now you can use this adapter in your List:
placesList.setAdapter(new PicassoAdapter(urls, DisplayPlacesActivity.this));
beware, that the performance of this code may be very bad, since we try to load the image on the UI-Thread, but I think it's ok as an example

android.view.WindowManager$BadTokenException list view set text then focus

This is my code:
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout viewGroup = (LinearLayout) findViewById(R.id.layout_popup);
final View layout = layoutInflater.inflate(R.layout.popup_layout,viewGroup);
final PopupWindow popup = new PopupWindow(GameActivity.this);
Point popupSize = new Point(getResources().getDimensionPixelSize(R.dimen.popup_width),getResources().getDimensionPixelSize(R.dimen.popup_height));
popup.setContentView(layout);
popup.setWidth(popupSize.x);
popup.setHeight(popupSize.y);
popup.setFocusable(true);
File dir = getFilesDir();
File temp = new File(dir.getPath()+"/temp.prj");
try {
if(!temp.exists())temp.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
File[] filesArray = dir.listFiles();
ArrayList<String> files = new ArrayList<>();
for(File file:filesArray)
{
files.add(file.getName());
}
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
popup.showAtLocation(layout, Gravity.NO_GRAVITY, (size.x-popupSize.x)/2, (size.y-popupSize.y)/2);
ListView listView = (ListView)layout.findViewById(R.id.listViewDir);
final EditText fileName = (EditText)layout.findViewById(R.id.editTextFilename);
final StableArrayAdapter adapter = new StableArrayAdapter(GameActivity.this,R.layout.list_item, files);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
final TextView item = (TextView) view;
fileName.setText(item.getText());
}
});
The bit that is causing me trouble is:
#Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
final TextView item = (TextView) view;
fileName.setText(item.getText());
}
Where after I have clicked on the item in the list view, I then go to click on the filename view and the exception
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W#53668b30 is not valid; is your activity running?
is thrown. I have tried running fileName.setText(item.getText()); on the UI thread, I have tried using MainActivity.this (or my variant of it, GameActivity.this) in numerous different places. Nothing seems to be working. Does anyone have any idea what's going wrong? All related questions seem to revolve around a Dialog rather than a Popup
UPDATE
For some unearthly reason, adding android:inputType="textNoSuggestions" to the EditText fileName in xml stops the exception. I only noticed this after prefixing the items in the listview with a number (which means they don't get spellchecked). It looks like someone else ran into a similar error Android ICS spelling error correction causes PopupWindow to crash and they too do not have an answer

ViewPager java.lang.OutOfMemoryError: bitmap size exceeds VM budget [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
java.lang.OutOfMemoryError: bitmap size exceeds VM budget - Android
I used ViewPager to show set of images from resource folder , if my images was small in size every thing works fine ,
but when i replace it with high definition images which i need it to be in my app , it gave me this error :
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
note 1 :
i have now 5 images in my code for testing but finally i will have around 30 high definition images ,
note 2 :
i wonder why this happen , i am new to android and first time to use viewpager class , before i used gallery class in another app with more than 30 high definition images and no exception happend .
any advice will be appreciated , thanks alot
my code :
logcat stack:
FATAL EXCEPTION: main
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:563)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:439)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:462)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:488)
at com.test.demo.MyPagerAdapter.<init>(MyPagerAdapter.java:42)
at com.test.demo.MainActivity.onCreate(MainActivity.java:15)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1615)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
at android.app.ActivityThread.access$1500(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
MainActivity
public class MainActivity extends Activity {
private ViewPager mMyPager;
private MyPagerAdapter mMyPagerAdapter;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mMyPager = (ViewPager) findViewById(R.id.mypages);
mMyPagerAdapter = new MyPagerAdapter(this);
mMyPager.setAdapter(mMyPagerAdapter); }}
MyPagerAdapter
public class MyPagerAdapter extends PagerAdapter {
private ArrayList<ImageView> mViewsList;
private Context mContext = null;
public MyPagerAdapter(Context context) {
mContext = context;
mViewsList = new ArrayList<ImageView>();
Resources resource = mContext.getResources();
Bitmap bMap1 = BitmapFactory.decodeResource(resource,
R.drawable.one);
ImageView image1 = new ImageView(mContext);
image1.setImageBitmap(bMap1);
mViewsList.add(image1);
Bitmap bMap2 = BitmapFactory.decodeResource(resource,
R.drawable.two );
ImageView image2 = new ImageView(mContext);
image2.setImageBitmap(bMap2);
mViewsList.add(image2);
Bitmap bMap3 = BitmapFactory.decodeResource(resource,
R.drawable.three);
ImageView image3 = new ImageView(mContext);
image3.setImageBitmap(bMap3);
mViewsList.add(image3);
Bitmap bMap4 = BitmapFactory.decodeResource(resource,
R.drawable.four);
ImageView image4 = new ImageView(mContext);
image4.setImageBitmap(bMap4);
mViewsList.add(image4);
Bitmap bMap5 = BitmapFactory.decodeResource(resource,
R.drawable.five);
ImageView image5 = new ImageView(mContext);
image5.setImageBitmap(bMap5);
mViewsList.add(image5);
}
#Override
public int getCount() {
return mViewsList.size();
}
#Override
public Object instantiateItem(View view, int position) {
View myView = mViewsList.get(position);
((ViewPager) view).addView(myView);
return myView;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public void destroyItem(View view, int arg1, Object object) {
((ViewPager) view).removeView((ImageView) object);
}
}
Your Adaptor should not be written the way you have it. You should only be decoding the bitmaps in the instantiateItem method.
private Context context;
private ArrayList<Integer> mResourceList;
private Resources resource;
public MyPagerAdapter(Context context) {
this.context = context;
resource = context.getResources();
mResourceList = new ArrayList<Integer>();
mResourceList.add(R.drawable.one);
mResourceList.add(R.drawable.two);
mResourceList.add(R.drawable.three);
mResourceList.add(R.drawable.four);
mResourceList.add(R.drawable.five);
}
#Override
public Object instantiateItem(View view, int position) {
ImageView myView = new ImageView(context);
Bitmap bitmap = BitmapFactory.decodeResource(resource, mResourceList.get(position) );
myView.setImageBitmap(bitmap);
((ViewPager) view).addView(myView);
return myView;
}
Now, you need to make sure that your bitmaps are not exceeding the max size value (2048px x 2048px).
If you are, you must scale your image down. This can be done by adding a BitmapFactory.Options object to your BitmapFactory.decodeResouce parameters and setting the inSampleSize by a power of 2. Setting it by 2 will sample it down to 50%, 4 to 25%, etc.
BitmapFactory.Options options = new BitmapFactory.Options()
options.inSampleSize = 2
Bitmap bitmap = BitmapFactory.decodeResource(resource, mResouceList.get(position), options );
Hope this helped!
Size of your bitmap is very large,So try to scale your bitmap before loading it.
Syntax for scaling bitmap
Bitmap bitmapName= Bitmap.createScaledBitmap(sourceBitmap, width, height,
true);
Where width and height are the size you want for the image to be.
Your issue is that you're creating a bunch of bitmaps before really needing to.
Inside your instantiateView method, create the ImageView's and load the bitmaps and set them there. If you need, use your ArrayList to hold your resource ints to know which images to load if you need. This way, garbage collection will remove bitmaps as needed, and shouldn't cause memory issues.

Categories

Resources