Swipecards icon indicating copy to clipboard operation
Swipecards copied to clipboard

Conflict with Picasso

Open Diolor opened this issue 9 years ago • 12 comments

It looks like there is a conflict with Picasso: If you initiate Picasso in the getView method of the adapter it requests the container (FlingSwipeAdapterView) continuously.

This will sap down the performance but the user probably will not notice it.

Will investigate more.

Diolor avatar Oct 02 '14 21:10 Diolor

Can you describe this issue in more detail? Did not encounter this issue or maybe just didn't notice yet?!

alopix avatar Oct 15 '14 13:10 alopix

I am using Universal Image Loader, and it is working fine with SwipeCards.

qichuan avatar Oct 19 '14 05:10 qichuan

I'm receiving OutOfMemory crashes on Google Play during beta testing of an app with your (awesome) library.

I'm also using Picasso to set card backgrounds in getView from adapter.

You have any suggestions how to prevent this? Txn.

(Report from Samsung Galaxy S4 with Android (4.4)

android.view.InflateException: Binary XML file line #17: Error inflating class at android.view.LayoutInflater.createView(LayoutInflater.java:626) at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) at android.view.LayoutInflater.onCreateView(LayoutInflater.java:675) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:700) at android.view.LayoutInflater.rInflate(LayoutInflater.java:761) at android.view.LayoutInflater.inflate(LayoutInflater.java:498) at android.view.LayoutInflater.inflate(LayoutInflater.java:398) at be.accentjobs.swop.ui.MainActivity$JobAdapter.getView(MainActivity.java:408) at flingswipe.SwipeFlingAdapterView.layoutChildren(SwipeFlingAdapterView.java:122) at flingswipe.SwipeFlingAdapterView.onLayout(SwipeFlingAdapterView.java:109) at android.view.View.layout(View.java:15655) at android.view.ViewGroup.layout(ViewGroup.java:4856) at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1055) at android.view.View.layout(View.java:15655) at android.view.ViewGroup.layout(ViewGroup.java:4856) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) at android.widget.FrameLayout.onLayout(FrameLayout.java:388) at android.view.View.layout(View.java:15655) at android.view.ViewGroup.layout(ViewGroup.java:4856) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531) at android.widget.LinearLayout.onLayout(LinearLayout.java:1440) at android.view.View.layout(View.java:15655) at android.view.ViewGroup.layout(ViewGroup.java:4856) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) at android.widget.FrameLayout.onLayout(FrameLayout.java:388) at android.view.View.layout(View.java:15655) at android.view.ViewGroup.layout(ViewGroup.java:4856) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2284) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2004) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1236) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6471) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803) at android.view.Choreographer.doCallbacks(Choreographer.java:603) at android.view.Choreographer.doFrame(Choreographer.java:573) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:157) at android.app.ActivityThread.main(ActivityThread.java:5356) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Constructor.constructNative(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at android.view.LayoutInflater.createView(LayoutInflater.java:600) ... 44 more Caused by: java.lang.OutOfMemoryError at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:677) at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:507) at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:872) at android.content.res.Resources.loadDrawable(Resources.java:3056) at android.content.res.TypedArray.getDrawable(TypedArray.java:602) at android.widget.ImageView.(ImageView.java:133) at android.widget.ImageView.(ImageView.java:123) ... 47 more

sanderversluys avatar Oct 24 '14 08:10 sanderversluys

Hi niob,

I have solved this problem in my pull request, pleasee see below link. https://github.com/Diolor/Swipecards/pull/15

qichuan avatar Oct 24 '14 23:10 qichuan

I'll remove my previous comment because that's not related to the Picasso issue. But thanks @qichuan for the tip!

sanderversluys avatar Oct 24 '14 23:10 sanderversluys

OutOfMemory is usually a sign of memory leak. The problem is that picasso looks like it creates an infinite loop when interfering with async tasks (infinite calls of requestLayout).

Sorry guys but I cant help these days much. Will help after a week or so that i will be back on track.

Diolor avatar Oct 26 '14 12:10 Diolor

Any update on this issue?

kamoljan avatar Nov 27 '14 09:11 kamoljan

Not really. I would be glad to review a pull request however if someone digs into that.

Diolor avatar Nov 28 '14 22:11 Diolor

Same problem (getView continuously) when using android query or universal imageloader

comment out 2 lines below in SwipeFlingAdapterView seems do some help

...
  if (mActiveCard != null && topCard != null && topCard == mActiveCard) {
//                removeViewsInLayout(0, LAST_OBJECT_IN_STACK);
//                layoutChildren(1, adapterCount);

            } ...

wtttc avatar Jan 09 '15 02:01 wtttc

Hi Gang,

I believe I have identified the cause and a solution to the issue of Picasso triggerring continuous calls to the getView method of the adapter.

First, the issue (for me) only arises when I use the .fit() method with Picasso. This method causes Picasso to wait until the ImageView is loaded, and dimensions are obtained, before making the network/in-memory call(s) necessary prior to calling setImageDrawable.

One of the idiosyncrasies of setImageDrawable on an ImageView is that it triggers an onMeasure and onLayout, according to this SO post.

If you were to avoid using the .fit() method, the call to Picasso.load.into would also call setImageDrawable and call requestLayout, but calls to requestLayout in the middle of a layout pass have no effect. However, if you defer the call to setImageBitmap, then you end up in a cycle where a layout pass is calling getView, which is calling Picasso, which does a deferred setImageBitmap, which (a few ms later) requests a layout pass, ad infinitum.

Why does this work normally with ListViewAdapters? I am not entirely sure, but something in this Swipecards implementation of onLayout is causing it to constantly create new views. I suspect that ListViewAdapters know how to "recycle" views, so that if you have a ListView of 5 items laid out, and you call onLayout, it won't re-create 5 views but will instead re-use the 5 views on screen and thus avoid triggering Picasso again.

Regardless - I have a proposed fix. In the nominal case, onLayout is repeatedly called until there is both an active and top card, meaning we end up in this code here:

if(mActiveCard!=null && topCard!=null && topCard==mActiveCard) {
    removeViewsInLayout(0, LAST_OBJECT_IN_STACK);
    layoutChildren(1, adapterCount);
}else{

This code has the effect of re-laying out the cards behind the top card, and is necessary for cards in the back to be aligned properly as the card in the front is moving, in the event that they interact. If you aren't actively dragging or manipulating the top card, you don't need to do anything with the layout, because the code just below will have already taken care of it. Thus, we need a new method to detect when the user is actively manipulating the top card. I added this code to FlingCardListener.java:

public boolean isTouching() {
    return this.mActivePointerId != INVALID_POINTER_ID;
}

public PointF getLastPoint() {
    return new PointF(this.aPosX, this.aPosY);
}

And then changed SwipeFlingAdapterView.java, lines 104-105, to this:

if(mActiveCard!=null && topCard!=null && topCard==mActiveCard) {
  if (this.flingCardListener.isTouching()) {
    PointF lastPoint = this.flingCardListener.getLastPoint();
    if (this.mLastTouchPoint == null || !this.mLastTouchPoint.equals(lastPoint)) {
      this.mLastTouchPoint = lastPoint;
        removeViewsInLayout(0, LAST_OBJECT_IN_STACK);
        layoutChildren(1, adapterCount);
    }
  }
}else{

I also added PointF mLastTouchPoint as a member variable. Per this change for me, this will now only call getView when the user is actively moving the top card or at Activity onCreate.

Cheers!

esilverberg avatar Jan 25 '15 00:01 esilverberg

The last comment fixes the issues for me too! Can someone fix this in the main rep? Would be awesome :)

regards Simon

simllll avatar Mar 31 '15 22:03 simllll

thank u @esilverberg bro. U save my day (Y) 🥇

sacdulanga avatar Jul 05 '17 05:07 sacdulanga