Android Glide - Cannot recycle a resource that has already been recycled - android

I have a list of objects the user can create and delete on runtime and each object is assigned an icon. When I do multiple add/delete operations on these objects at some point I get the following exception.
java.lang.IllegalStateException: Cannot recycle a resource that has already been recycled
at com.bumptech.glide.load.engine.EngineResource.recycle(EngineResource.java:71)
at com.bumptech.glide.load.engine.ResourceRecycler$ResourceRecyclerCallback.handleMessage(ResourceRecycler.java:37)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:135)
I perform no recycle operations on my own and I do not make any calls to Glide bitmap pool. My custom Glide model is the following class
public class MyGlideInput {
private String packageName, apkFilePath, iconResName;
public MyGlideInput() {
}
#Override
public boolean equals(#Nullable Object obj) {
if(obj == null){
return false;
}
if(!(obj instanceof MyGlideInput)){
return false;
}
MyGlideInput input = (MyGlideInput) obj;
return stringsEqual(packageName, input.packageName ) && stringsEqual(apkFilePath, input.apkFilePath)
&& stringsEqual(iconResName, input.iconResName);
}
#Override
public int hashCode() {
return (apkFilePath+packageName+iconResName+"").hashCode();
}
public boolean stringsEqual(String a, String b){
return a != null && a.equals(b);
}
}
on RecyclerView adapter class in bindViewHolder I do:
GlideInput glideInput = new GlideInput().setDrawableResName(iconResName);
GlideApp.with(img).load(glideInput).dontAnimate().error(R.drawable.warning).into(img);
Any suggestions?

From what I have seen until now, this error appears mostly because of transformations if you have any. I had some recycling in transform method even though it's stated that developers should not do it by themselves in their custom transformations. Also, you should be really careful in RecycleView.
Can you provide us with more of code, e.g. some transformations that you use or whatever you think it can have some bad impact?

Related

How to reference an Activity from within an Aspect

I currently have the current aspect
#Aspect
public class ActivityShowingAspect {
private static final String POINTCUT_METHOD =
"execution(#nz.co.kevinsahandsomedevil.android.myaccount.aspect.ActivityMustBeShowing * *(..))";
#Pointcut(POINTCUT_METHOD)
public void methodAnnotatedWithActivityShowing() {
}
#Around("methodAnnotatedWithActivityShowing()")
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
Activity activity = // code to retrieve the calling activity, joinPoint.getTarget() or whatever
Object result = null;
if(!activity.isFinishing()) {
result = joinPoint.proceed();
} else {
result = // do something else
}
return result;
}
}
I'd like to know how to determine the calling Activity from within the Aspect.
Okay so it depends on where the method with your annotation is.
If the annotated method is declared within an Activity implementation, then you can indeed call joinpoint.getTarget()and cast the result.
Also you might want to update your pointcut to make sure that the method indeed is on an activity :
execution(#nz.co.vodafone.android.myaccount.aspect.ActivityMustBeShowing * *(..)) && within(android.app.Activity+)
If that's not the case then you might need to add an advice before any activity's onResume() to remember what the current activity is.

Create boolean listener

In Android, how do I take an action whenever a variable changes?
So I want to implement a listener for an object I created. What I want it to do is execute a block of code when its value changes from false to true.
As I am following this thread, I can't understand where the person wants us to implement the last block of code containing the logic for the listener.
Could someone, hopefully, guide me in the right direction?
(This question is being asked here as I don't have enough rep. points)
That last bit of example code triggers the listener, so it basically needs to be run whenever the "event" occurs. In this case the "event" is whenever (wherever in the code) the value of the variable changes.
If you have a setter and that is the only place the value changes, that is where you'd put it. If you are changing the value in multiple places throughout your code, I would make a new private method (call it signalChanged), put your code there, and then call it immediately after the variable assignment in the cases you want the listener to fire.
Here's an example (some code borrowed from linked answer, haven't checked that it compiles).
public class MyObj
{
public MyObj(int value)
{
setValue(value);
}
private int myValue;
public int getValue() { return myValue; }
public void setValue( int value )
{
if (value != myValue)
{
myValue = value;
signalChanged();
}
}
public interface VariableChangeListener
{
public void onVariableChanged(Object... variableThatHasChanged);
}
private VariableChangeListener variableChangeListener;
public void setVariableChangeListener(VariableChangeListener variableChangeListener)
{
this.variableChangeListener = variableChangeListener;
}
private void signalChanged()
{
if (variableChangeListener != null)
variableChangeListener.onVariableChanged(myValue);
}
}
you have to create a callback interface
here is a good about custom listener tutorial
here is a sample
public class MyObj {
VariableChanger onVariableChanged ;
public void setOnVariableChanged(VariableChanger onVariableChanged) {
this.onVariableChanged = onVariableChanged;
}
void log(){
boolean changed = false;
onVariableChanged.onVariableChanged();
//this will call it
}
interface VariableChanger{
void onVariableChanged();
}
}
class logic {
MyObj mo = new MyObj();
void main(){
mo.setOnVariableChanged(new MyObj.VariableChanger() {
#Override
public void onVariableChanged() {
//do your action
}
});
}
}
In Android, like any language, most developper uses logic comparisons to check values (if, else, switch, =, !=, >, <, etc) or Event (signal)
What kind of listener do you want to implement?

Android Smack with MVP fragment

I am using smack (XMPP library) and Mosby's MvpFagment to show the roster of a user in a listview (his/her connections).
I got the following code which works in a different fragment just doing a network call using the Retrofit library:
public void loadListData(final List<NeighbourListItemModel> itemsList) {
Log.e("list", itemsList.size() + "");
listViewAdapter.clear();
listViewAdapter.addThemAll(itemsList);
listViewAdapter.notifyDataSetChanged();
}
Which gets called by a RosterListener using roster.addRosterListener(new RosterListener() { .. });
The NeighbourListItemModel is just a simple POJO class having some getters and setts.
However, this gives a AbstractXMPPConnection﹕ Exception in async packet listener
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. error, probably because the XMP connection runs in its own thread.
Now if I change the code to the following:
#Override
public void loadListData(final List<NeighbourListItemModel> itemsList) {
Log.e("list", itemsList.size() + "");
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
listViewAdapter.clear();
listViewAdapter.addThemAll(itemsList);
listViewAdapter.notifyDataSetChanged();
}
});
}
I get a java.lang.NullPointerException: Attempt to invoke virtual method 'int getLayout()' on a null object reference error. Where getLayout is defined in:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolderAbstractClass viewHolder;
if (null == convertView || null == convertView.getTag()) {
viewHolder = getItem(position).getDefaultViewHolder(mItemType);
convertView = LayoutInflater.from(getContext()).inflate(viewHolder.getLayout(), null);
viewHolder.findViewsById(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolderAbstractClass) convertView.getTag();
}
BaseModel previousItem = position > 0 ? getItem(position - 1) : null;
viewHolder.setup(getItem(position), previousItem, mCaller.getContext(), position);
return convertView;
}
The view holder abstract class is nothing special:
public abstract class ViewHolderAbstractClass {
abstract public int getLayout();
abstract public void findViewsById(View view);
abstract public void initializeComponentBehavior(BaseModel item, Context context, int position);
public void setup(BaseModel item, BaseModel previous_item, Context context, int position) {
initializeComponentBehavior(item, context, position);
}
}
So obviously the viewHolder variable is null, but I have no idea why. My adapter is defined as new BaseListAdapter(this, R.layout.neighbours_list_item, new ArrayList<ChatItemModel>(), Constants.DATA_TYPE.CHAT);
Like I said, the former code is working when I make a call using Retrofit, but I suspect XMPP running in its own thread is giving me headaches.
The issue was that the Constants.DATA_TYPE itemType was referring to another model instead of a NeighbourListItemModel which caused a null return. The stacktrace did not show this clearly, but at least it is solved now.
1) I think this code viewHolder = getItem(position).getDefaultViewHolder(mItemType) is a suspect. It seems the code is caching the ViewHolder class and the problem is you cannot cache or save the UI IDs. I am sure you notice other developers simply create an instance of ViewHolder.
Besides that, please post code for getDefaultViewHolder.
2) Another, I suspect findViewsById() method could not work because it seems it is trying to cache UI IDs to find the correct View object.
** This issue of caching UI IDs is more noticeable now (!) because you are getting IDs on a separate thread.
You may however update the UI views on a separate UI thread. And you can get the UI IDs on a separate thread, but don't cache it, use it right away.

Use of Target in Picasso on Adapter

Im having big troubles using a Target inside an adapter. Im confused about the documentation on the code
Objects implementing this class must have a working implementation of
{#link #equals(Object)} and {#link #hashCode()} for proper storage internally. Instances of this
interface will also be compared to determine if view recycling is occurring. It is recommended
that you add this interface directly on to a custom view type when using in an adapter to ensure
correct recycling behavior.
Im trying to use the Target in this way:
class CustomTarget implements Target {
private ImageView imageView;
public CustomTarget(ImageView imageView) {
this.imageView = imageView;
}
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
imageView.setImageDrawable(new RoundedAvatarDrawable(bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
imageView.setImageDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
imageView.setImageDrawable(placeHolderDrawable);
}
#Override
public boolean equals(Object o) {
return imageView.equals(o);
}
#Override
public int hashCode() {
return imageView.hashCode();
}
}
#Override
public View getView(int position, View v, ViewGroup parent) {
....
RoundedAvatarDrawable r = new RoundedAvatarDrawable(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_avatar_seahorse));
ImageCacheController.with(mContext).getPicasso().load(member.getPicture_url()).resize(100, 100).centerCrop().placeholder(r).error(r).into(new CustomTarget(viewHolder.ivAvatar));
....
}
It's doesn't work and the images change between each others randomly
You don't show your whole getView function, so without knowing how you use the viewHandler, here's my take on what's going on:
Your problem is that you're creating a new CustomTarget every time getView gets called. You are going against the point of having a Target object. Let me elaborate.
When a new download request is made, previous requests to the same target get stopped or don't result in a call to the Target's callbacks. (so if the Target gets reused for a different row in a list it doesn't get both rows' images).
You are using a new object for each request, effectively hinting Picasso that each request is for a different row so to speak. The doc says "Instances of this interface will also be compared to determine if view recycling is occurring", so since each request has a newly created CustomTarget object, no two requests will have the same object and a row recycle won't be detected.
You're also using viewHolder. In this case I think the viewHolder should be extending the Target interface (if you only have 1 image per row). This way everytime you request a download you can use the same object and not create a new one.
You're also delegating the implementation of your CustomTarget to the ImageView's implementation. Make sure that ImageView's equals and hashCode functions fullfill the requirements Picasso asks for.
Some info on how to implement equals and hashCode: What issues should be considered when overriding equals and hashCode in Java?
It seems your equals method is broken. You are comparing an imageview to a custom target. This might fix it:
public boolean equals(Object o) {
if(o instanceof CustomTarget) {
return ((CustomTarget) o).imageView.equals(this.imageView);
}
return super.equals(o);
}

Where to write logical code for my model?

I've got a ListActivity with about 100 events. (These events are also displayed on a map in another activity.)
So I've my MyListActivity, which handles the list and a MyListAdapter to populate the list that deals with MyEvent-Objects. As model I have a MyEvent-model-Class and a MyEventStorage-Class.
Now have written a method to return an image for an event based on its ID. It does some decisions which image to load, where it gets the image from, loads it and resamples it.
Where should I put this method in best practice?
I don't want to copy it in every activity where it is needed but just in one place.
I'd like to have it in my MyEvent-Class, so I can call myEvent.getImage(); but it somehow feels wrong to put this method inside the model class with all the getters and setters. Is it wrong?
Should I write a helper class containing this method? As a static method? Would this still provide a good performance?
Or maybe create an additional MyImageGetter-object for every MyEvent-object?
Or expand the MyEvent-model with an image-variable and getters/setter and create an extra class that puts the proper image in the model? How would I call that method?
Another solution?
MyEvent.java:
public class MyEvent {
private int id;
private int category;
private String eventname;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// other getters and setters
}
MyEventStorage.java:
private static MyEventStorage instance = null;
private List<MyEvent> store;
private MyEventStorage() {
store = new ArrayList<MyEvent>();
}
// get the storage containing the events
public static MyEventStorage getInstance() {
if (instance == null) {
instance = new MyEventStorage();
}
return instance;
}
public List<MyEvent> getStore() {
return store;
}
public void setStore(List<MyEvent> store) {
this.store = store;
}
// Add a Event to the store
public void addEvent(MyEvent myEvent) {
store.add(myEvent);
}
// Remove a Event from the store
public void removeEvent(MyEvent myEvent) {
store.remove(myEvent);
}
}
The method I want to integrate:
Image getImageById(int id) {
// decide which image to load based on the events id
// decide where to load the image from
// check if image available
// load image if available else load placeholder image
// resample image
return image;
}
Thank you advance!
I think your last bullet point is spot on.
If the Image is in fact a property of MyEvent, it makes sense to add an instance variable to that class. You shouldn't include the logic for retrieving an event's image from a datasource in the model, but rather use a static utility method to load this property.
Your getImageById method looks like it has to do a decent amount of work to retrieve the image from wherever it is stored. I think it would make the most sense to create a utility class (along the lines of ImageRetriever) like you mentioned in order to perform the actual retrieval of the image. This prevents you from having to copy the method to multiple places. Performance should not be a concern either, as you'll never have to instantiate this class.
The code could look something like this:
public class MyEvent {
private int id;
private int category;
private String eventname;
private Image image;
public MyEvent(int id...) {
// initialize instance vars
setImageFromRetriever();
}
public void setImage(Image image) {
this.image = image;
}
public void setImageFromRetriever() {
// optional null check if you don't want to reload images
setImage(ImageRetriever.getImageById(this.id));
}
}

Categories

Resources