Butterknife: Inject fails even though I only have one view for my fragment

Created on 11 Sep 2014  路  4Comments  路  Source: JakeWharton/butterknife

Hello.
I have been seeing a crash happening to a handful of my app's users.
I get the IllegalStateException at Butterknife.findRequiredView, saying it can't find my field, but there is only one view file for my fragment.
In my InjectView list, it fails on the first one, leading me to suspect it's the view file that's not being found, or somehow it's pointing to the wrong one.
I haven't been able to reproduce this myself, so any insight would be greatly appreciated.

Here are some specs for the flow:

  1. The crashes so far occur on API 15 (Jellybean).
  2. The fragment uses the model-view-presenter design pattern.
  3. I call a refresh on my onStart of the fragment (but only if the presenter has already been set), which redoes the Butterknife.inject call where the crashes occur.
  4. I do have an addition Butterknife.inject I call in my Activity that contains the fragment, to hold a SwipeRefreshLayout (who's setOnRefreshListener does the refresh). I read some posts on how you should not inject in the parent and the child, but I wasn't sure this extended to activity and fragment, since my team and I have never seen this crash ourselves with this setup.
  5. the view file does use to add a reusable view, among which is the view containing the failing field. I don't think this should be a problem, but I'm adding it here just in case.

Lastly the stack trace.
java.lang.RuntimeException: Unable to inject views for com.betterfinance.smartpay.presenter.HomeOfferView@41800310
at butterknife.ButterKnife.inject(ButterKnife.java:263)
at butterknife.ButterKnife.inject(ButterKnife.java:196)
at com.betterfinance.smartpay.presenter.HomeOfferView.setupViews(HomeOfferView.java:77)
at com.betterfinance.smartpay.presenter.HomeOfferView.refresh(HomeOfferView.java:73)
at com.betterfinance.smartpay.presenter.HomeOfferPresenter.refresh(HomeOfferPresenter.java:49)
at com.betterfinance.smartpay.fragment.HomeOfferFragment.onStart(HomeOfferFragment.java:85)
at android.app.Fragment.performStart(Fragment.java:1536)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:862)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1032)
at android.app.BackStackRecord.run(BackStackRecord.java:622)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1382)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:426)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4477)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:555)
at dalvik.system.NativeStart.main(NativeStart.java)
Caused by: java.lang.IllegalStateException: Required view with id '2131296341' for field 'txtApprovedFunds' was not found. If this view is optional add '@Optional' annotation.
at butterknife.ButterKnife$Finder.findRequiredView(ButterKnife.java:122)
at com.betterfinance.smartpay.presenter.HomeOfferView$$ViewInjector.inject(HomeOfferView$$ViewInjector.java:10)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:511)
at butterknife.ButterKnife.inject(ButterKnife.java:254)
at butterknife.ButterKnife.inject(ButterKnife.java:196)
at com.betterfinance.smartpay.presenter.HomeOfferView.setupViews(HomeOfferView.java:77)
at com.betterfinance.smartpay.presenter.HomeOfferView.refresh(HomeOfferView.java:73)
at com.betterfinance.smartpay.presenter.HomeOfferPresenter.refresh(HomeOfferPresenter.java:49)
at com.betterfinance.smartpay.fragment.HomeOfferFragment.onStart(HomeOfferFragment.java:85)
at android.app.Fragment.performStart(Fragment.java:1536)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:862)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1032)
at android.app.BackStackRecord.run(BackStackRecord.java:622)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1382)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:426)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4477)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:555)
at dalvik.system.NativeStart.main(NativeStart.java)

Thanks!

Most helpful comment

This also happened to me as I am an Android n00b.
The problem is simple.
It will occur when ButterKnife.inject is called before setContentView.
I just have to change the calling order. Problem solved.

All 4 comments

I don't really know what to tell you. Butter Knife isn't really doing anything here except calling findViewById on whatever View you pass in. There's not a whole bunch that can go wrong inside of Butter Knife itself.

Even if you weren't using Butter Knife you'd get a NullPointerException crash when you tried to use the value from a manual findViewById call.

You could try/catch the Butter Knife inject method and then add additional information to the crash report (like dump the view hierarchy or something).

Thank you for the suggestions. I'll keep poking around.

This also happened to me as I am an Android n00b.
The problem is simple.
It will occur when ButterKnife.inject is called before setContentView.
I just have to change the calling order. Problem solved.

May be some late time, but, if in your custom view u did'n set setId like

String android_schemas = "http://schemas.android.com/apk/res/android";
int idImage = attrs.getAttributeIntValue(android_schemas, "id", -1);
if (idImage > -1) {
this.setId(idImage);
}

When your view will not have id attribute value.

Was this page helpful?
0 / 5 - 0 ratings