Open oniekrenz opened 11 years ago
I'll have to try and reproduce. It might simple be a problem with how IDEA does incremental compilation that requires me to jump through many more hoops.
If you do a full clean build it probably still works though, right?
Correct
I think this is just a fundamental flaw in annotation processing-based code generation. Changing a class should cause the processor to run again for it but changing the hierarchy is too-complex a task for an incremental generation. Hopefully this doesn't happen too much. It's just something we are going to have to deal with.
I believe it happens any time you make changes to the subclass tho, not (just) when you change the class hierarchy.
Right, it happens on any change.
I can re-open, but I'm not really going to be working toward fixing this unless something obvious and easy presents itself.
Just as an added note: I've also noticed this happening on multiple projects of mine.
Haven't had this happen since I moved to Gradle.
I came across the same problem. Anyone know the reason of this behaviour? Is there a solution aside from using gradle or rebuilding the whole project?
FWIW, I have come across this. The issue I was trying to solve was a way to abstract out a universal sliding menu for the entire application. My Activities extended an abstract BaseMenuActivity, which handed all the building of the Drawer and event listeners.
BaseMenuActivity.onCreate would call getViewId(), which was an abstract method implemented by the subclass. It would then do the view injection. It would work if there was a full rebuild, but not if there was a change in either class. I guess I will just have to rework it.
Thanks for such a nice library.
Moved to maven, works like charm.
I'm using IntelliJ & maven, it is still happening for me.
Moved to Android Studio, and all the problems are gone
This is untrue. The issue persists and is insanely annoying. Whenever I change a class that has a superclass with injected views or a class which has a subclass with injected views, I build apps that compile perfectly fine and run into random nullpointerexceptions at runtime. This is not something you should just shrug away.
The issue is open. Also pull requests welcome!
great content
@JakeWharton Can we close this now? I have been using Butterknife for a long time. I can inject views such as the NavigationDrawer
in a BaseClass
and subclass it in SubCass
. This is how I inherit the NavigationDrawer to all my Activities and Fragments.
@gte619n if you are still having this problem:
/**
* @author <a href="mailto:jaredsburrows@gmail.com">Jared Burrows</a>
*/
@SuppressWarnings("checkstyle:visibilitymodifier")
public abstract class BaseActivity extends AppCompatActivity implements OnNavigationItemSelectedListener {
protected DrawerLayout drawerLayout;
protected NavigationView navigationView;
protected AppBarLayout appBar;
protected Toolbar toolbar;
private static final int NAV_DRAWER_LAUNCH_DELAY = 250;
private final Handler handler = new Handler();
private ActionBarDrawerToggle drawerToggle;
/**
* @return Layout Id for the Activity.
*/
public abstract int getLayoutResId();
@Override
public boolean onNavigationItemSelected(final MenuItem menuItem) {
final int itemId = menuItem.getItemId();
this.onNavDrawerItemClicked(itemId);
this.navigationView.setCheckedItem(itemId);
this.closeNavDrawer();
return true;
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(this.getLayoutResId());
this.drawerLayout = (DrawerLayout) this.findViewById(R.id.drawer_layout);
this.appBar = (AppBarLayout) this.findViewById(R.id.app_bar_layout);
this.toolbar = (Toolbar) this.findViewById(R.id.tool_bar);
this.navigationView = (NavigationView) this.findViewById(R.id.navigation_view);
}
// Must call this for drawer toggle to work correctly
@Override
protected void onPostCreate(final Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Setup Toolbar
try {
if (this.toolbar != null) {
this.setToolbarNavigationIcon(R.drawable.ic_menu_white_24dp);
this.setSupportActionBar(this.toolbar);
}
} catch (final Throwable e) {
LogUtils.logE(TAG, "Sadly, Robolectric blows up when this method is called.", e);
}
// Setup DrawerLayout
if (this.drawerLayout != null && this.toolbar != null) {
this.drawerToggle = new ActionBarDrawerToggle(this, this.drawerLayout, this.toolbar, R.string.nav_open,
R.string.nav_close);
this.drawerLayout.addDrawerListener(this.drawerToggle);
// Sync the toggle state after onRestoreInstanceState has occurred.
this.drawerToggle.syncState();
}
// Setup NavigationView
if (this.navigationView != null) {
this.navigationView.setNavigationItemSelectedListener(this);
this.navigationView.setCheckedItem(R.id.menu_nav_home);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
this.handler.removeCallbacksAndMessages(null);
}
@Override
public void onConfigurationChanged(final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
this.drawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
this.getMenuInflater().inflate(R.menu.menu_activity_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
// Pass the event to ActionBarDrawerToggle, if it returns
// true, then it has handled the app icon touch event
if (this.drawerLayout != null && this.drawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case android.R.id.home:
if (this.drawerLayout != null) {
this.drawerLayout.openDrawer(GravityCompat.START);
} else {
this.finish();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBackPressed() {
if (this.isNavDrawerOpen()) {
this.closeNavDrawer();
} else {
super.onBackPressed();
}
}
public void setToolbarTitle(final CharSequence title) {
if (this.toolbar != null) {
this.toolbar.setTitle(title);
}
}
public void setToolbarSubtitle(final CharSequence subtitle) {
if (this.toolbar != null) {
this.toolbar.setSubtitle(subtitle);
}
}
public void setToolbarNavigationIcon(final int resId) {
if (this.toolbar != null) {
this.toolbar.setNavigationIcon(resId);
}
}
protected boolean isNavDrawerOpen() {
return this.drawerLayout != null && this.drawerLayout.isDrawerOpen(GravityCompat.START);
}
protected void openNavDrawer() {
if (this.drawerLayout != null) {
this.drawerLayout.openDrawer(GravityCompat.START);
}
}
protected void closeNavDrawer() {
if (this.drawerLayout != null) {
this.drawerLayout.closeDrawer(GravityCompat.START);
}
}
private void onNavDrawerItemClicked(final int id) {
this.handler.postDelayed(new Runnable() {
@Override
public void run() {
goToNavDrawerItem(id);
}
}, NAV_DRAWER_LAUNCH_DELAY);
}
private void goToNavDrawerItem(final int id) {
if (id == R.id.menu_app_settings) {
this.startActivity(new Intent(this, SettingsActivity.class));
} else {
this.closeNavDrawer();
}
}
}
How does this issue relate to https://code.google.com/p/android/issues/detail?id=200043? If google fixes that bug, will it also fix this issue?
No idea. Maybe. It depends on what information is given to the processor in the incremental compilation.
On Wed, Jul 6, 2016 at 4:29 PM Cypress Frankenfeld notifications@github.com wrote:
How does this issue relate to https://code.google.com/p/android/issues/detail?id=200043? If google fixes that bug, will it also fix this issue?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/JakeWharton/butterknife/issues/31#issuecomment-230896478, or mute the thread https://github.com/notifications/unsubscribe/AAEEEUG3d8zRfhtBSEpxy8Ahgd88RKCBks5qTBAvgaJpZM4An8m2 .
I'm using IDEA12. Setup is: class B extends A, both classes have some @InjectView fields. A "Rebuild Project" creates: A$$ViewInjector and B$$ViewInjector, the latter with a call to A$$ViewInjector.inject(finder, target, source) I edit B and deploy to my device. Compiling starts and the call to A$$ViewInjector.inject() gets removed -> App crashes because my injected fields in B are now null.