alexrainman / CarouselView

CarouselView control for Xamarin Forms
MIT License
436 stars 178 forks source link

[BUG][Android] Crash when CarouselView is nested in a ScrollView, inside a TabViewItem (XCT) #592

Open Kapusch opened 3 years ago

Kapusch commented 3 years ago

Description

Hello @alexrainman , I have been using your component for a few months, after I've got rid off the official CarouselView (so many events issues...) and I have to say : thank you very much for your great library !

But the thing is I am now using the Xamarin.CommunityToolkit (XCT) library so that I can navigate to my pages with bottom tabs. And my app is getting crashed every time I try to display your CarouselView inside a ScrollView.

I am actually having a java.lang.StackOverflowError :

Expand Stacktrace ``` [Mono] Assembly Ref addref CarouselView.FormsPlugin.Droid[0xce8f4de0] -> Java.Interop[0xce5c9d80]: 22 [w_stackoverflo] java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: JNI GetObjectClass called with pending exception java.lang.StackOverflowError: stack size 8192KB [w_stackoverflo] java_vm_ext.cc:570] at void crc6414252951f3f66c67.CarouselViewAdapter_2.n_onBindViewHolder(androidx.recyclerview.widget.RecyclerView$ViewHolder, int) (CarouselViewAdapter_2.java:-2) [w_stackoverflo] java_vm_ext.cc:570] at void crc6414252951f3f66c67.CarouselViewAdapter_2.onBindViewHolder(androidx.recyclerview.widget.RecyclerView$ViewHolder, int) (CarouselViewAdapter_2.java:38) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(androidx.recyclerview.widget.RecyclerView$ViewHolder, int, java.util.List) (RecyclerView.java:7065) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(androidx.recyclerview.widget.RecyclerView$ViewHolder, int) (RecyclerView.java:7107) [w_stackoverflo] java_vm_ext.cc:570] at boolean androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(androidx.recyclerview.widget.RecyclerView$ViewHolder, int, int, long) (RecyclerView.java:6012) [w_stackoverflo] java_vm_ext.cc:570] at androidx.recyclerview.widget.RecyclerView$ViewHolder androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(int, boolean, long) (RecyclerView.java:6279) [w_stackoverflo] java_vm_ext.cc:570] at android.view.View androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(int, boolean) (RecyclerView.java:6118) [w_stackoverflo] java_vm_ext.cc:570] at android.view.View androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(int) (RecyclerView.java:6114) [w_stackoverflo] java_vm_ext.cc:570] at android.view.View androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(androidx.recyclerview.widget.RecyclerView$Recycler) (LinearLayoutManager.java:2303) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(androidx.recyclerview.widget.RecyclerView$Recycler, androidx.recyclerview.widget.RecyclerView$State, androidx.recyclerview.widget.LinearLayoutManager$LayoutState, androidx.recyclerview.widget.LinearLayoutManager$LayoutChunkResult) (LinearLayoutManager.java:1627) [w_stackoverflo] java_vm_ext.cc:570] at int androidx.recyclerview.widget.LinearLayoutManager.fill(androidx.recyclerview.widget.RecyclerView$Recycler, androidx.recyclerview.widget.LinearLayoutManager$LayoutState, androidx.recyclerview.widget.RecyclerView$State, boolean) (LinearLayoutManager.java:1587) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler, androidx.recyclerview.widget.RecyclerView$State) (LinearLayoutManager.java:640) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2() (RecyclerView.java:4134) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.RecyclerView.dispatchLayout() (RecyclerView.java:3851) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget.RecyclerView$Adapter.bindViewH03-13 12:05:07.875 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void androidx.recyclerview.widget.RecyclerView.onLayout(boolean, int, int, int, int) (RecyclerView.java:4404) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.ItemsViewRenderer_3.n_onLayout(boolean, int, int, int, int) (ItemsViewRenderer_3.java:-2) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.ItemsViewRenderer_3.onLayout(boolean, int, int, int, int) (ItemsViewRenderer_3.java:45) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] 03-13 12:05:07.875 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.Platform_DefaultRenderer.n_onLayout(boolean, int, int, int, int) (Platform_DefaultRenderer.java:-2) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.Platform_DefaultRenderer.onLayout(boolean, int, int, int, int) (Platform_DefaultRenderer.java:72) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void androidx.recyclerview.widget03-13 12:05:07.875 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.Platform_DefaultRenderer.n_onLayout(boolean, int, int, int, int) (Platform_DefaultRenderer.java:-2) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.Platform_DefaultRenderer.onLayout(boolean, int, int, int, int) (Platform_DefaultRenderer.java:72) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void com.xamarin.forms.platform.android.FormsViewGroup.measureAndLayout(int, int, int, int, int, int) (FormsViewGroup.java:37) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.ItemsViewRenderer_3.onLayout(boolean, int, int, int, int) (ItemsViewRend:07.875 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.PageRenderer.n_onLayout(boolean, int, int, int, int) (PageRenderer.java:-2) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.PageRenderer.onLayout(boolean, int, int, int, int) (PageRenderer.java:72) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void com.xamarin.forms.platform.android.FormsViewGroup.measureAndLayout(int, int, int, int, int, int) (FormsViewGroup.java:37) [w_stackoverflo] java_vm_ext.cc:570] at void crc643f46942d9dd1fff9.PlatformRenderer.n_onLayout(boolean, int, int, int, int) (PlatformRenderer.java:-2) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_03-13 12:05:07.875 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void android.widget.RelativeLayout.onLayout(boolean, int, int, int, int) (RelativeLayout.java:1103) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverfl03-13 12:05:07.876 F] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.LinearLayout.setChildFrame(android.view.View, int, int, int, int) (LinearLayout.java:1829) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) 03-13 12:05:07.876 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void android.widget.LinearLayout.layoutVertical(int, int, int, int) (LinearLayout.java:1673) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.View.layout(int, int, int, int) (View.java:22160) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.Vi03-13 12:05:07.876 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332) [w_stackoverflo] java_vm_ext.cc:570] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270) [w_stackoverflo] java_vm_ext.cc:570] at void com.android.internal.policy.DecorView.onLayout(boolean, int, int, int, int) (DecorView.java:810) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6402) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewRootImpl.performLayout(android.view.WindowManager$LayoutParams, int, int) (ViewRootImpl.java:3330) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewRootImpl.performTraversals() (ViewRootImpl.java:2826) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.03-13 12:05:07.876 F/w_stackoverflo(12828): java_vm_ext.cc:570] at void android.view.ViewRootImpl.doTraversal() (ViewRootImpl.java:1901) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.ViewRootImpl$TraversalRunnable.run() (ViewRootImpl.java:8066) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.Choreographer$CallbackRecord.run(long) (Choreographer.java:1041) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.Choreographer.doCallbacks(int, long) (Choreographer.java:860) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.Choreographer.doFrame(long, int) (Choreographer.java:785) [w_stackoverflo] java_vm_ext.cc:570] at void android.view.Choreographer$FrameDisplayEventReceiver.run() (Choreographer.java:1026) [w_stackoverflo] java_vm_ext.cc:570] at void android.os.Handler.handleCallback(android.os.Message) (Handler.java:914) [w_stackoverflo] java_vm_ext.cc:570] at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:100) [w_stackoverflo] java_vm_ext.cc:570] at void android.os.Looper.loop() (Looper.java:225) [w_stackoverflo] java_vm_ext.cc:570] at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:7563) [w_stackoverflo] java_vm_ext.cc:570] at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:539) [w_stackoverflo] java_vm_ext.cc:570] at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:994) [w_stackoverflo] java_vm_ext.cc:570] [w_stackoverflo] java_vm_ext.cc:570] in call to GetObjectClass [w_stackoverflo] java_vm_ext.cc:570] from void crc6414252951f3f66c67.CarouselViewAdapter_2.n_onBindViewHolder(androidx.recyclerview.widget.RecyclerView$ViewHolder, int) ```

I have tried to catch the error by debugging both Xamarin.CommunityToolkit and your CarouselView library, but I was only getting a crash without having being able to get a hint...

Also, I can see a related issue #560 which seems to be fixed in 6.0.0. Then I may think that your fix works if the content is loaded in a ContentPage, but maybe not in a ContentView ?

Steps to Reproduce

  1. Run the repo on Android
  2. Clicks on the third tab where it says "ScrollView + Carousel"
  3. You can see the app is crashing and gives a StackOverflowError in Application Output

Basic Information

Workaround

  1. Replacing ScrollView with a grid works fine, but I really need the ScrollView in my page.
  2. Load the tab content in a new page, but I lose the benefit of using bottom tabs.

Reproduction Link

CarouselView_Stackoverflow.zip

Kapusch commented 3 years ago

Maybe it's related to this https://github.com/xamarin/XamarinCommunityToolkit/issues/1104 ?

Kapusch commented 3 years ago

Hi, I actually got the issue from an endless recursive loop in the CarouselViewImplementation.

Indeed, the method Android.App.Activity FindActivity(Context context) is continuously called itself here:

var contextThemeWrapper = _context as Android.Views.ContextThemeWrapper;
if (contextThemeWrapper != null)
{
    return FindActivity(contextThemeWrapper.BaseContext);
}

as it is not able to find the MainActivity :/

Do you think you could have a look at it please @alexrainman ?

Kapusch commented 3 years ago

Here are some logs in case that helps a little bit:

Console logs for a working Carousel controller ``` -------> Call of FindActivity from CarouselViewRenderer -------> FindActivity called 1 times -------> Rercursive call of FindActivity -------> FindActivity called 2 times -------> Call of FindActivity from SetNativeView -------> FindActivity called 3 times -------> Rercursive call of FindActivity -------> FindActivity called 4 times ```
Console logs for crashing Carousel controller ``` -------> Call of FindActivity from CarouselViewRenderer -------> FindActivity called 1 times -------> Rercursive call of FindActivity -------> FindActivity called 2 times -------> Rercursive call of FindActivity -------> FindActivity called 3 times -------> Rercursive call of FindActivity -------> FindActivity called 4 times ....... -------> Rercursive call of FindActivity -------> FindActivity called a billion times ```
Kapusch commented 3 years ago

Actually @alexrainman , I think it is because the same object is passed through the recursive call.

Indeed, _context.BaseContext is always passed, so if MainActivity is not found during the first recursive call, then it will trigger an endless loop.

Kapusch commented 3 years ago

Hello @alexrainman , sorry to spam you, but I think I have fixed my issue by replacing https://github.com/alexrainman/CarouselView/blob/eda4fd3757297a619aef4518af2a1da441402440/CarouselView/CarouselView.FormsPlugin.Android/CarouselViewImplementation.cs#L82

with

var contextThemeWrapper = context as Android.Views.ContextThemeWrapper;

But I'm only able to validate the fix by running your demo project... Actually, I'm not sure if the new NuGet I have generated is valid :/

Could you please help me to understand how to directly refer your source code into my repro project ?

alexrainman commented 3 years ago

Isn't var contextThemeWrapper = _context as Android.Views.ContextThemeWrapper; the same as var contextThemeWrapper = context as Android.Views.ContextThemeWrapper;?