1

Why does passing custom parameters to a parent activity through an onCreate method, while leaving overriding the root method to the parent, result in the following linter error:

Overriding method should call super.onCreate

Background

For example, I have a MainActivity class that extends from ParentActivity that extends Android's Activity.

example inheritance

UML Generator

In order to make my app more abstract I am trying to handle several things in ParentActivity that the developer need not see in their development and use of MainActivity.

I have several parameters I would like to pass to the ParentActivity, like booleans turning on and off log functionalities, but it seems passing them through the onCreate() method is not ~~possible~~ recommended since this throws linter errors. I will make a separate question regarding the best practices for passing such parameters upward to custom parent classes using custom methods or directly setting parent fields, but I was looking to verify my current understanding of why this is not ~~possible~~ recommended through the existing onCreate method and additional parameters.

What has been tried

As a starting point, if I have some basic MainActivity and ParentActivity:

Code 0.1

public class MainActivity extends ParentActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        //Do stuff
    }
}
public class ParentActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        //Do stuff
    }
}

and then try to add further parameters to onCreate, e.g. a boolean to turn on/off some logger functionality within ParentActivity:

Code 0.2

public class MainActivity extends ParentActivity{
    boolean logOn = true;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState, logOn);
        //Do stuff
    }
}
public class ParentActivity extends Activity{
    // No longer overriding Activity.onCreate() due to diff in params
    protected void onCreate(Bundle savedInstanceState, boolean logOn){
        super.onCreate(savedInstanceState);
        //Do stuff
    }
}

Android Studio first warns me that I am not overriding the parent's method, which makes sense as it has a different parameter count, but then I thought I can just remove the @Override and call it good since I'm still calling super.onCreate(savedInstanceState) in ParentActivity, which will pass the savedInstanceState up to Activity, and I'm still passing the savedInstanceState to ParentActivity from MainActivity. At this point I encountered my first unknown issue: back in MainActivity, I get a linter error that states

Overriding method should call super.onCreate

Whats confusing is that I do call super.onCreate(savedInstanceState, logOn) directly below where I get this error. Although the error message is not too informative, I can get rid of the error by calling super.onCreate(savedInstanceState) directly above the already existing call to super.onCreate(savedInstanceState, logOn), i.e.:

Code 0.3

public class MainActivity extends ParentActivity{
    boolean logOn = true;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        super.onCreate(savedInstanceState, logOn);
        //Do stuff
    }
}
public class ParentActivity extends Activity{
    // No longer overriding Activity.onCreate() due to diff in params
    protected void onCreate(Bundle savedInstanceState, boolean logOn){
        super.onCreate(savedInstanceState);
        //Do stuff
    }
}

Looking at the tooltip inline doc using Android Studio, I see that super.onCreate(savedInstanceState); is calling the onCreate method from Activity (i.e the parent class of ParentClass) and super.onCreate(savedInstanceState, logOn); is calling the onCreate method of ParentClass. With typical inheritance in mind, and matching parameter lengths and types, this makes sense.

What doesn't make sense to me is why I have to call the onCreate method of Activity in MainActivity. Why does the call to onCreate within ParentActivity not suffice? i.e. why does Code 0.2 throw the linter error:

Overriding method should call super.onCreate

? I note, as per the comment by @greeble31 that Code 0.2 compiles, and runs on my example smartphone, but the linter error remains.

topher217
  • 1,188
  • 12
  • 35
  • @greeble31, yes you are right, I updated the question to correct the wording from compiler to linter. I will work on rewriting the question and title to better include this difference. – topher217 Nov 26 '19 at 03:16

2 Answers2

1

I don't think it's too much to worry about, since the problem is limited to a lint warning. You just did something a little too complicated for the linter to follow; your program is not, in fact, incorrect.

You could suppress the warning (@SupressLint), or simply ignore it.

...I can just remove the @Override and call it good...

Not too sure I agree with you, there... Removing the @Override annotation can't really help anything; the linter/compiler still knows what's an override and what's not. I consider the annotation useful b/c the IDE will tell you if you think you're overriding a method, but you're actually not, i.e. due to a signature mismatch or something (as here).

SUGGESTED APPROACH

FWIW, I would've solved this problem a little differently. (Note that code 0.3 actually results in two calls to the base class onCreate(); that's probably illegal.) I would just change the method name (to reflect a semantic distinction between configuration and creation), and store some state information in the base class:

public class MainActivity extends ParentActivity{
    boolean logOn = true;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        configure(logOn);    //Required, per base class specification
        super.onCreate(savedInstanceState);
        //Do stuff
    }
}
public abstract class ParentActivity extends Activity{

    boolean logOn;
    boolean configured = false;

    /** Subclasses are obligated to call this before calling super.onCreate() */
    protected void configure(boolean logOn)
    {
        this.logOn = logOn;
        this.configured = true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState){
        if(!configured)
            throw new IllegalStateException("configure() not called prior to onCreate()");

        super.onCreate(savedInstanceState);
        //Do stuff
    }

    @Override
    protected void onDestroy() {
        configured = false;    //(just being pedantic)
    }
}

It's really just a matter of taste.

Sometimes people don't realize that Android can spontaneously create Activities on its own, like when it restores the back-stack state of your app. (This is why you want to be able to serialize and deserialize your Activity state to/from the bundle; b/c your Activity might not be re-created using the same workflow that caused its creation the first time).

I don't think that's going to be an issue for you, though, since ParentActivity (which I've declared as abstract) is always going to be instantiated via a concrete subclass, and all subclasses are guaranteed to call configure() in their onCreate() methods. (IOW, ParentActivity wouldn't have a manifest entry, so the system is never going to try to instantiate a base-class ParentActivity by itself.)

Just something to be aware of. (You had logOn set to a constant value in MainActivity, so if you were planning on changing that dynamically based on the app state, before calling configure()/super.onCreate(), just bear in mind that -- if you don't take steps to prevent it -- that information could be lost when your app is restored to the foreground.)

greeble31
  • 4,894
  • 2
  • 16
  • 30
  • The comment regarding the removal of the @Override call was not an attempt to fool the linter, but more to point out that I was no longer overriding ParentActivity's method, but rather Activitiy's. I suppose that is not really correct either though :/. – topher217 Nov 28 '19 at 08:28
  • Yes, your solution is what I was leaning towards as well, and what I was hinting at when I said I'd "make a separate question regarding the best practices for passing such parameters upward to custom parent classes using custom methods" I was just concerned with tidiness really, as I didn't want it to end up looking something like onCreate() followed by onCreate2()...Exaggeratedly bad naming there, but you get the point I guess. – topher217 Nov 28 '19 at 08:28
  • Nope, the logOn was just something to be set for debug builds only. Thanks for the tips though! – topher217 Nov 28 '19 at 08:28
0

IDE will check @CallSuper annotation when on compilation {@link Activity#onCreate}.

vikash singh
  • 1,479
  • 1
  • 19
  • 29
leesin
  • 1
  • 1