0

The entire original question was based on a mistake. You can NEVER assign the value of a final static FIELD constant in the constructor. If you could, you would have the value of the static FIELD changing every time a new instance of the class was created. Not very final!

I can instead do what I have been doing (and possibly creating memory leaks, I have a lot to learn), which is to use a static (but not final) field to point to the most recent instance of my Activity (see the code sample below). As the comment below that talks of memory leaks points out, I almost certainly have been creating memory leaks by keeping references to my older Activity objects around.

So the answers to my original question really comes down to: this is not a good thing to do because handing other objects references to my current Activity object allows them to keep the system from ever garbage collecting my old Activity objects!

Anyway, here is the code referred to in my original question, the answers and comments are still here so the rest of this is kept for reference to those.

public class MyActivity extends Activity {
  public final static MyActivity uI;
  public static MyActivity mostRecentUI;

  private MyActivity() { // this is the constructor
      // In my original post I had:
      // uI = this;  
      // but this is illegal code.  `final static anything;` cannot be assigned a 
      // value in the constructor, because it could be assigned a different value
      // each time the class is instantiated!  

      // Instead the best I can do is
      mostRecentUIobject = this;
      // and this name better describes what this static variable contains
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
  }
}

Added 4/15: @dmon goes as far as to comment that the constructor won't be run. It will, and anybody with any doubt can run this test activity:

public class TestActivityConstructor extends Activity {
    static long refTime = System.currentTimeMillis();
    static String staticExecution = "Static never executed\n";
    static String constructorExecution = "Constructor never executed\n";
    static String onCreateExecution = "onCreate never executed\n";

    static {
        staticExecution = "Static Execution at " + (System.currentTimeMillis() - refTime) + " ms\n";
    }

    public TestActivityConstructor() {
        constructorExecution = "Constructor Execution at " + (System.currentTimeMillis() - refTime) + "ms \n";
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        onCreateExecution = "onCreate Execution at " + (System.currentTimeMillis() - refTime) + "ms \n";
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ((TextView) findViewById(R.id.TV)).setText(staticExecution + constructorExecution + onCreateExecution);
    }
}

Obviously, you need a trivial layout.xml with a textview called TV. No permissions needed. You can even have fun rotating your android to see the app recreated showing that both the constructor and the onCreate are re-run every time the screen is rotated, but that the static assignments are NOT redone when the Activity is recreated.

mwengler
  • 2,738
  • 1
  • 19
  • 32
  • 1
    Not sure what are the requirements to do so? – Lion Apr 14 '12 at 18:23
  • 1
    what are you trying to achieve? – Andreas Linden Apr 14 '12 at 18:26
  • 1
    `final MyActivity uI; private MyActivity() { uI = this; }` this code is quite useless. It is just an other name for `this` – stefan bachert Apr 14 '12 at 19:00
  • The point is without using a constructor, no local fields can be declared "final." Assuming final isn't just a bad part of Java that Android is right to assasinate, the point is that a private constructor can let you use "final" variables and fields in your Activity code. – mwengler Apr 14 '12 at 22:36
  • I edited the example and the text to make it more obvious what the use of declaring a field final in an Activity would be. I use uI extensively for accessing views and buttons back on the user interface from other classes. – mwengler Apr 14 '12 at 22:43
  • 1
    don't save Activity into a static to use from outside, that a [leak](http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/) – zapl Apr 14 '12 at 22:54
  • "The point is without using a constructor, no local fields can be declared final" -- sure they can. Ordinary initializers work just fine with `final`, so long as you do not try to use inherited methods from `Activity` (which generally will not work until `super.onCreate()` has finished). – CommonsWare Apr 14 '12 at 23:43
  • @mwengler And how might you propose your private constructor gets called by the system? Were you planning on placing your activity in the same package as the system? If that's the case, then you'd need to make your own custom build of the platform so that it could check for your activity and if it finds it call the "special" constructor. That would be rather silly. – dcow Apr 15 '12 at 00:09
  • @zapl that is a great link on [memory leaks](http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/)! My own apps I actually turn off the autorotate because I haven't figured out how to deal with Activity being created and destroyed constantly. Obviously I have a lot to learn. – mwengler Apr 15 '12 at 22:11

3 Answers3

3

To basically answer the main part of your question title...

OK to use private constructor in Activity...?

No.

I'm assuming you're coming to Android from a previous Java stand-point but one thing to understand about Android is that the Activity class is one of a few special case classes which should be left to work as they're designed to.

Don't concern yourself with the concept of constructors when it comes to the Activity class just follow the design methodologies and application fundamentals which work quite adequately.

If you have a specific requirement which doesn't work within the Android framework then post a question with example code and somebody will explain how to fix it.

EDIT: In light of your update to your question...

public final static MyActivity uI;

With this in place, I can refer back to the "user interface" object as MyActivity.uI without having to pass references through chains of calls.

Seriously...don't do that. There are perfectly adequate, functional and safe mechanisms which can be used to easily handle any situation with minimal code without 'breaking' the model in the way you're attempting to do things.

To repeat what I said above in my original answer - the Android Activity class is a special case. Instances of Activity and their methods/fields are not meant to be accessed directly by any other external class.

You'll be doing yourself a favour and save a lot of headaches if you simply accept the fundamentals of the Android framework. Coding Android apps may well involve Java but not all 'generic' Java concepts apply, in particular to some of the core components such as Activity.

Squonk
  • 48,735
  • 19
  • 103
  • 135
  • @mwengler This may not be the answer you want but it is the correct answer. You can still create static final member variables -- which should be all you need. – dcow Apr 15 '12 at 00:06
  • @David I can create static final variables but I can't assign them information like context that doesn't exist until runtime. How else am I going to keep from having everything in the MyActivity class if I don't put text in views and so on from other classes? Any good books on this I should read? – mwengler Apr 15 '12 at 21:50
  • @mwengler you don't need static final classes. Your activities all subclass context which you can retrieve views from. If you need a context in something else, then you will have to pass it. The idea is that you should do all you view editing and control in the Activity classes since they are already the context under which you would need to work. You should write helper classes only when you can partition the work into something that can be done outside of an Android context -- OR if it makes logical sense to have a class that accesses the views, again you pass the context. – dcow Apr 16 '12 at 01:13
  • You'll most likely need to do both. However, if you find yourself always needing to write classes that require contexts, you probably need to rework your class system so that it adheres to the Android style idioms. As far as good books, I like the O'Reily _Programming Android_. However, the SDK is a great place to start if you want something free. – dcow Apr 16 '12 at 01:16
2

Since the activity manager instanciates your Acitivity you probably don't get your constructor called - not sure if that works with a private contructor + final.

Also several things can't be used at construction time since your activity is not completely initialized until onCreate is called (like SharedPreferences).

zapl
  • 63,179
  • 10
  • 123
  • 154
  • Yeah, that constructor won't get called. – dmon Apr 14 '12 at 18:30
  • @dmon you should actually run a test activity before you make incorrect claims. I'll add code I used to test it in my original post above. – mwengler Apr 15 '12 at 21:52
  • Cmon guys, ... think about what you are writing, how could the constructor possibly not be called? Seriously! What you are right though is that several things can't be used before `onCreate` being called, and that it is not very recommendable to use it. – Levite Mar 15 '17 at 12:05
  • 1
    @Levit Hmm, yep, obviously there is no way to have instances without the constructor getting executed. Maybe what I tried to say is that the activity manager can't call `Class#newInstance()` (see http://stackoverflow.com/questions/28636597/android-activity-constructor) when you have a private constructor (android expects a public no-args one). – zapl Mar 15 '17 at 12:14
0

Fine, your constructor might be getting called. However, there's no "single" activity (not really), so setting a final variable that references an instance of your activity is NOT the right way to go about this. If you support orientation changes, for example, the activity will get recreated (as you have found out), and your final reference is wrong. Also, since you have a reference to a destroyed activity (that is no longer used), you've just leaked that activity. I'm afraid you're gonna have to go through the normal methods of creating and instantiating views.

Anyway, I think a better question is an example of what you're trying to accomplish (and why you found it so troublesome), rather than trying to side-step what's already in place.

dmon
  • 30,048
  • 8
  • 87
  • 96
  • You are exactly right. The general thing I am trying to accomplish is to avoid having the code in my Activity grow without bounds since virtually anything I want to see on the screen seems to have to be in my original Activity subclass. THat is, unless I can pass references to my the instance of my Activity which is running, and thus add menus and onclick listeners and so on from other classes, defined in other .java files. My main activity now is 245 lines long even using this memory leak prone referencing to other classes. – mwengler Apr 15 '12 at 23:11
  • Yeah, it's a bit annoying that all of the listeners, etc, have to go in the main activity class (you can move those off to other classes, but there's little point of that), but that's life with Android. It gets better as you start using fragments. You can also create custom components if you find yourself rehashing the same idea over and over again. – dmon Apr 15 '12 at 23:59