anvil-ui / anvil

Minimal UI library for Android inspired by React
MIT License
1.45k stars 90 forks source link

onDetachedFromWindow is called twice #95

Open amoikevin opened 7 years ago

amoikevin commented 7 years ago

The method "onDetachedFromWindow" of RenderableView is called twice,this will cause some exceptions. For example, RecyclerView will throw NullPointerException, for mGapWorker was set to null at the first time.

iciakky commented 7 years ago

+1 I have exact the same issue. For now I explicitly unmount my RenderableViews, so that they won't be detach twice. BTW this issue is probably related to the #82

zserge commented 7 years ago

Could you please give a bit more context? Are these renderable views located inside other renderable views? Or maybe they are adapter items? Or are they mounted directly into the activity?

FYI, in the upcoming Anvil 0.5.0 (see forge branch for now) I'm hoping to add umount(View v, boolean removeChildViews) to allow unounting RenderableViews without destroying the view hierarchy. Might help in some cases when Android removes child views by itself and doesn't expect Anvil to do so.

iciakky commented 7 years ago

For the stack trace, please see the first reply of #82, I've seen it before. To reproduce this issue, launch this(gist) and tap the back key to exit this app, then comes the NPE: (the anvil version I'm using is 0.4.0)

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.GapWorker.remove(android.support.v7.widget.RecyclerView)' on a null object reference
                      at android.support.v7.widget.RecyclerView.onDetachedFromWindow(RecyclerView.java:2489)
                      at android.view.View.dispatchDetachedFromWindow(View.java:15560)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3187)
                      at android.view.ViewGroup.removeViewsInternal(ViewGroup.java:4825)
                      at android.view.ViewGroup.removeViews(ViewGroup.java:4681)
                      at trikita.anvil.Anvil.unmount(Anvil.java:138)
                      at trikita.anvil.RenderableView.onDetachedFromWindow(RenderableView.java:33)
                      at android.view.View.dispatchDetachedFromWindow(View.java:15560)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3187)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3179)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3179)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3179)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3179)
                      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3179)
                      at android.view.ViewRootImpl.dispatchDetachedFromWindow(ViewRootImpl.java:3259)
                      at android.view.ViewRootImpl.doDie(ViewRootImpl.java:5917)
                      at android.view.ViewRootImpl.die(ViewRootImpl.java:5894)
                      at android.view.WindowManagerGlobal.removeViewLocked(WindowManagerGlobal.java:446)
                      at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:384)
                      at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:124)
                      at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4243)
                      at android.app.ActivityThread.-wrap6(ActivityThread.java)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1538)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6119)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
FunnyDevs commented 7 years ago

any workaround???

zserge commented 7 years ago

I'm not sure how this issue has been affected by Anvil 0.5.1, but there is now an option to call Anvil.unmount(view, removeChildViews), so if the issue was caused by the fact that child views has already been removed - then perhaps changing the removeChildViews flag to false in the RenderableView class would help.

FunnyDevs commented 7 years ago

The problem comes with recyclerview. The only solution, i think, is create a Custom Renderable View and put "Anvil.unmout(this,false)" inside "onDetachedFromWindow"

zserge commented 7 years ago

Hm.. So RecyclerView injects some child views on its own, then during the unmount() call we remove its child views and when it tries to do the same - it crashes. I wonder if custom unmount(v, false) works any better, and if so - perhaps unmount() can be rewritten to only remove Anvil own views (much like end() does).

pardom-zz commented 7 years ago

I'm getting the same exception as @iciakky

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.GapWorker.remove(android.support.v7.widget.RecyclerView)' on a null object reference
  at android.support.v7.widget.RecyclerView.onDetachedFromWindow(RecyclerView.java:2534)
  at android.view.View.dispatchDetachedFromWindow(View.java:15560)
  at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3187)
  at android.view.ViewGroup.removeViewsInternal(ViewGroup.java:4825)
  at android.view.ViewGroup.removeViews(ViewGroup.java:4681)
  at trikita.anvil.Anvil.unmount(Anvil.java:181)
  at trikita.anvil.Anvil.unmount(Anvil.java:166)
  at trikita.anvil.RenderableView.onDetachedFromWindow(RenderableView.java:33)
  at clean.news.ui.item.list.ItemListView.onDetachedFromWindow(ItemListView.kt:38)
  at android.view.View.dispatchDetachedFromWindow(View.java:15560)
  at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3187)
  at android.view.ViewGroup.removeViewInternal(ViewGroup.java:4715)
  at android.view.ViewGroup.removeViewAt(ViewGroup.java:4665)
  at android.support.v7.widget.RecyclerView$5.removeViewAt(RecyclerView.java:732)
  at android.support.v7.widget.ChildHelper.removeViewAt(ChildHelper.java:168)
  at android.support.v7.widget.RecyclerView$LayoutManager.removeViewAt(RecyclerView.java:7915)
  at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleViewAt(RecyclerView.java:8187)
  at android.support.v7.widget.LinearLayoutManager.recycleChildren(LinearLayoutManager.java:1363)
  at android.support.v7.widget.LinearLayoutManager.recycleViewsFromStart(LinearLayoutManager.java:1409)
  at android.support.v7.widget.LinearLayoutManager.recycleByLayoutState(LinearLayoutManager.java:1478)
  at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1502)
  at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1325)
  at android.support.v7.widget.LinearLayoutManager.scrollHorizontallyBy(LinearLayoutManager.java:1049)
  at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:4722)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
  at android.view.Choreographer.doCallbacks(Choreographer.java:686)
  at android.view.Choreographer.doFrame(Choreographer.java:618)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
  at android.os.Handler.handleCallback(Handler.java:751)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:154)
  at android.app.ActivityThread.main(ActivityThread.java:6119)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

I'm rewriting my best practices/example app to use Anvil instead of RxBinding and this is blocking me.

autonomousapps commented 7 years ago

See this bug report filed on the Android issue tracker: https://issuetracker.google.com/issues/38375597

If you star it, maybe it'll get more attention and get fixed.

chriscoderdr commented 6 years ago

In my case I could fix it without modifying the recyclerview or downgrading the version I have my recyclerview inside a custom compound view and I was doing inflater.inflate(R.layout.layout_name, this, true); in the constructor now instead of doing it I'm inflating it this way mView = inflater.infltate(R.layout.layout_name, this, false); and after that adding the view with this.addView(mView);

and on my onDetachedFromWindow I have this:

if (mView != null) {
            removeView(mView);
        }

It works like a charm.

meikaiss commented 6 years ago

support-v7:27.1.0 fix this bug

CK875430315 commented 6 years ago

@meikaiss are you sure? it it still void someone said

CK875430315 commented 6 years ago

@meikaiss i am sry,recyclerview27.1.0 fixed the mGapWorker is null