Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anvil.currentView() is null inside render method #83

Closed
danhawkes opened this issue Sep 21, 2016 · 8 comments · Fixed by #99
Closed

Anvil.currentView() is null inside render method #83

danhawkes opened this issue Sep 21, 2016 · 8 comments · Fixed by #99
Assignees

Comments

@danhawkes
Copy link

Any ideas why the following exception is being thrown by withId()?

Caused by: java.lang.RuntimeException: Anvil.currentView() is null
at trikita.anvil.BaseDSL.withId(BaseDSL.java:688)

The issue occurs in the render cycle immediately after the view in question is removed from its parent, like this:

Pass 1:

linearLayout(() -> {
    v(SomeView.class, () -> {});
    v(SomeView.class, () -> {});
    v(SomeView.class, () -> {});
}) 

Pass 2:

linearLayout(() -> {
    v(SomeView.class, () -> {});
    // Removed
    v(SomeView.class, () -> {});
}) 

SomeView's render method where the crash is thrown is essentially this:

@Override
public void view() {
    xml(R.layout.some_view, () -> {
        // Crash here
        withId(R.id.button, () -> {
        }
    }
@zserge
Copy link
Collaborator

zserge commented Sep 21, 2016

Well, it definitely sounds like a bug, but to be honest - Anvil is not so good at adding/removing identical views on the fly. The reason is that it uses a very basic algorithm for diffing (to keep code short and to make it fast): if the class matches - adjust the attributes, otherwise - replace the view. E.g. if you populate the attributes of the views and remove the 2nd view - it will remove the last (3rd) view and re-apply the attributes of the 3rd view to the 2nd view instead. That's why in practice it makes more sense to either use different classnames or just toggle the visibility flag without changing the number of views. Or use adapters.

Anyway, I'll try to reproduce it in the meantime, thanks for reporting!

@danhawkes
Copy link
Author

Thanks for the quick reply.
Have done some testing regarding the number of child views, and it also seems to happen when removing the sole child from the container (so the diff will remove the view rather than re-purpose it). I don't think(?) anvil should be calling SomeView's view() method at this point as it should have been unmounted.

@mitchryanjusay
Copy link

@zserge

Any update on this?

@zserge
Copy link
Collaborator

zserge commented Nov 5, 2016

@mitchryanjusay To speed things up a reproducible test case or some crash logs would be helpful.

@mitchryanjusay
Copy link

mitchryanjusay commented Nov 5, 2016

@zserge

java.lang.RuntimeException: Anvil.currentView() is null
at trikita.anvil.BaseDSL.withId(BaseDSL.java:688)
at spanda.com.core.ui.fragments.views.ViewInfoPagerLogin$view$1.view(ViewInfoPagerLogin.kt:16)
at trikita.anvil.BaseDSL.xml(BaseDSL.java:676)
at spanda.com.core.ui.fragments.views.ViewInfoPagerLogin.view(ViewInfoPagerLogin.kt:15)
at trikita.anvil.Anvil$Mount.render(Anvil.java:204)
at trikita.anvil.Anvil.render(Anvil.java:173)
at trikita.anvil.Anvil.render(Anvil.java:104)
at spanda.com.core.ApplicationContext$onCreate$1.run(ApplicationContext.kt:70)
at trikita.jedux.Store$1.dispatch(Store.java:32)
at trikita.jedux.Store$2.dispatch(Store.java:45)
at spanda.com.core.redux.middlewares.UIHandler.dispatch(UIHandler.kt:22)
at spanda.com.core.redux.middlewares.UIHandler.dispatch(UIHandler.kt:20)
at trikita.jedux.Store$3.dispatch(Store.java:53)
at spanda.com.core.redux.middlewares.NetworkHandler.dispatch(NetworkHandler.kt:40)
at spanda.com.core.redux.middlewares.NetworkHandler.dispatch(NetworkHandler.kt:24)
at trikita.jedux.Store$3.dispatch(Store.java:53)
at spanda.com.core.redux.middlewares.Logger.dispatch(Logger.kt:17)
at trikita.jedux.Store$3.dispatch(Store.java:53)
at trikita.jedux.Store.dispatch(Store.java:60)
at spanda.com.core.redux.middlewares.NetworkHandler$reduceLoginAction$request$1.onCompleted(NetworkHandler.kt:55)
at com.facebook.GraphRequest$1.onCompleted(GraphRequest.java:304)
at com.facebook.GraphRequest$5.run(GraphRequest.java:1383)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:102)

In my current code I have a view pager and that viewpager I have fragments that contain TextInputLayout that of course, has TextInputEditText in it. All the layouts are made with XML and state and data binding done through anvil. Anvil.render() is triggered as a subscriber to Jedux Store.

I've tried debugging the code to see where the problem lies and the problem seems to be in startNode (Anvil.java). On the second time it renders (first time being the onCreateView) the node's children size is 0 hence it doesn't enter the condition
if (node.viewIndex >= 0 && node.viewIndex < vg.getChildCount()) { node.view = vg.getChildAt(node.viewIndex); }

then when it goes back to BaseDSL.java startFromLayout if the node's id is equal to the ID pass it's immediately return without checking if the node's view is null.

For now, placing all the contents of the view method (of the class that extends RenderableView) inside a try-catch avoids crashes but of course would not render all of the updates to the state/data binding

@defHLT
Copy link

defHLT commented Nov 15, 2016

@danhawkes does it happen when a view is removed by some code calling "removeView" etc only? (vs via a condition in Anvil layout)

@danhawkes
Copy link
Author

I'm not manually adding/removing views, but am thinking it's possible that the RecyclerView (via RecyclerViewAdapter) might be.

@zserge
Copy link
Collaborator

zserge commented Feb 21, 2017

In fact, I think this is likely to be fixed very soon.
I'm now working on Anvil 0.5.0 (see anvil-forge branch). That will be using real views instead of virtual nodes, so it gets rid of the issues like this. Also, I've made some other minor changes and the following code now works perfectly (I hope I understood the original use case correctly): https://gist.github.com/zserge/0c066cfa7039632da8e2b530dc69f765

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants