Butterknife: Generated ViewBinding doesn't extend from library module ViewBinding

Created on 18 Nov 2016  Â·  7Comments  Â·  Source: JakeWharton/butterknife

There's problem with extend views (Activities, Fragments, etc.) from library module and overriding inflated layout. Unfortunately your great, etc. library doesn't catch this kind of situations so generated ViewBinding for one module doesn't extends from another one.

In example situation ActivityB_ViewBinding should extends from ActivityA_ViewBinding

public class ActivityB_ViewBinding<T extends ActivityB> implements Unbinder {

```java
public class ActivityA_ViewBinding implements Unbinder {

should be
```java
public class ActivityB_ViewBinding<T extends ActivityB> extends ActivityA_ViewBinding<T> {

```java
public class ActivityA_ViewBinding implements Unbinder {

app module:
---
```java
public class ActivityB extends ActivityA {
    @BindView(R.id.text_b) TextView textB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        textB.setText("test");
    }

    @LayoutRes
    @Override
    protected int getLayoutRes() {
        return R.layout.activity_b;
    }
}

gradle includes:

annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
compile project(':lib')

lib module:

public abstract class ActivityA extends Activity {
    @BindView(R2.id.text_a) TextView textA;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutRes());
        ButterKnife.bind(this);
        // textA is null when ActivityA is extended in other module!
        textA.setText("test");
    }

    @LayoutRes
    protected int getLayoutRes() {
        return R.layout.activity_a;
    }
}

gradle includes:

compile 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'

activity_b.xml layout of course have 2 TextView with R.id.text_a and R.id.text_b. Unfortunatelly ButterKnife doesn't have anything against when we forget to include layout with R.id.text_a - generator validator doesn't work at compile time here.

logs:

Looking up binding for com.test.ActivityB
HIT: Loaded binding class and constructor.

This issue probably is connected to https://github.com/JakeWharton/butterknife/issues/771 and https://github.com/JakeWharton/butterknife/issues/698 but I'm not sure about that.

Most helpful comment

We faced this issue after the division of the app into several UI modules. As a workaround we've added to the super class an explicit call to the corresponding ViewBinder
Using names from the OP question:

public abstract class ActivityA extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutRes());
        ButterKnife.bind(this);
        unbinder = new ActivityA_ViewBinding<ActivityA>(this);
        textA.setText("test");
    }
}

@JakeWharton If I understand correctly this is currently the limitation of the library, right? Are you aware of any other workarounds or our solution is the way to go until you support this scenario in the library?

All 7 comments

To solve this problem author should add methods

@NonNull 
@UiThread
public static Unbinder bind(@NonNull Activity target, Class<?> tClass) {
    View sourceView = target.getWindow().getDecorView();
    return createBinding(target, sourceView, tClass);
}

@NonNull
@UiThread
public static Unbinder bind(@NonNull Object target, View sourceView, Class<?> tClass) {
    return createBinding(target, sourceView, tClass);
}

to ButterKnife class.
When you want to bind your Activity (or any View), on your BaseActivity you should bind View like this:

ButterKnife.bind(this);
if (!this.getClass().getName().contains("YOUR_MIAN_PACKETNAME_OF_MODULE")) {
    ButterKnife.bind(this, this.getClass().getSuperclass());
}

In first line is binded your class ActivityB, in next we check that your class is from out of app-module (lib-module). If if return true we are call bind method for super class of ActivityB - in this case for ActivityA

This method works for Activity, Fragment and other Views which have it's parent in other module in project

No, that requires the caller have knowledge of whether or not their base
class uses ButterKnife or not and whether it's present in the same module
or not. Additionally, it doesn't handle two base classes from two separate
libraries that extend each other.

On Fri, Dec 2, 2016 at 11:48 AM PanBaklazan notifications@github.com
wrote:

To solve this problem author should add methods

@NonNull @UiThreadpublic static Unbinder bind(@NonNull Activity target, Class tClass) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView, tClass);
}
@NonNull@UiThreadpublic static Unbinder bind(@NonNull Object target, View sourceView, Class tClass) {
return createBinding(target, sourceView, tClass);
}

to ButterKnife class.
When you want to bind your Activity (or any View), on your BaseActivity
you should bind View like this:

ButterKnife.bind(this);if (!this.getClass().getName().contains("YOUR_MIAN_PACKETNAME_OF_MODULE")) {
ButterKnife.bind(this, this.getClass().getSuperclass());
}

In first line is binded your class ActivityB, in next we check that your
class is from out of app-module (lib-module). If if return true we are
call bind method for super class of ActivityB - in this case for ActivityA

This method works for Activity, Fragment and other Views which have it's
parent in other module in project

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/JakeWharton/butterknife/issues/808#issuecomment-264501667,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEb3LrR23kMaTU7KVexRo4VaxVZA5ks5rEEvygaJpZM4K2oko
.

Yes, it doesn't handle two base classes from two separate libraries. My solution is little hack and works if it is our modules (app and library) and we know about that ActivityB from app have parent ActivityA from lib module, but if lib-module which we extend, uses ButterKnife and we have no access to modify code - this method will not works

We faced this issue after the division of the app into several UI modules. As a workaround we've added to the super class an explicit call to the corresponding ViewBinder
Using names from the OP question:

public abstract class ActivityA extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutRes());
        ButterKnife.bind(this);
        unbinder = new ActivityA_ViewBinding<ActivityA>(this);
        textA.setText("test");
    }
}

@JakeWharton If I understand correctly this is currently the limitation of the library, right? Are you aware of any other workarounds or our solution is the way to go until you support this scenario in the library?

I think this issue is the same as : https://github.com/JakeWharton/butterknife/issues/958

The problem is that BK expects all annotated elements to be passed by the compiler process() method, and this is not true in the case of a super class in a separate module. Moreover, the annotations are not visible anymore.

Though, I think it should be possible for BK to deal with this situation: when a "parent" class is scanned for annotations, BK could also try to load the TypeElement corresponding to this class View Binder (usingElements.getTypeElemement()). If the view binder exists, it means the class was already processed in a module, which means subclasses should call this view binder.

--> the check should take place here: check if parent type is in the parent set OR there is a view binder for this parent. If there is a view binder, an entry should be added to the bindingMap for the parent class. The code in BindingSet.newBuilder seems to be working. Probably a field should be added to prevent this new BindingSet to try to generate a view binder.

Then, BK will start having another issue: precompiled modules could be an old jar using an incompatible version of BK... This can also be solved by adding a BK version number field to the view binders.

Is there a next release date slated that will include this?

No. But soon.

On Mon, Sep 9, 2019 at 3:27 PM Benjamin Wicks notifications@github.com
wrote:

Is there a next release date slated that will include this?

—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
https://github.com/JakeWharton/butterknife/issues/808?email_source=notifications&email_token=AAAQIEPAC4UXTDBOEFD2EUDQI2PTTA5CNFSM4CW2REUKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD6IYELQ#issuecomment-529629742,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAAQIEIBYYGKXWMWOLP2F7TQI2PTTANCNFSM4CW2REUA
.

Was this page helpful?
0 / 5 - 0 ratings