12

Daily there are many questions of the following type on SO:

How do I get a variable from another Activity?

The answers usually recommend to use SharedPreferences or Intent.putExtra().

To me, a getter method is what would be an approach to access a variable from another class. After all, the Activity that is under consideration is a class, and it's variables are class members.

Why aren't getter methods preferred to approaches like SharedPreferences or Intent extras?

I'm talking about simple situations that require accessing a variable between activities, for example this one:

class OneClass extends Activity {
    int a;

    ..
    // some changes to a
    ..
}

And then in another class(Activity):

class SomeOtherClass extends Activity {
    ..
    // trying to access a here
    ..
}

Is a getter method a correct approach here, or not?

Again - I'm not talking about scenarios where these things are actually the right way to go. SharedPreferences for persistent storage of small amount of data, extras as the documentation says : This can be used to provide extended information to the component. For example, if we have a action to send an e-mail message, we could also include extra pieces of data here to supply a subject, body, etc.


As some of the answers have likely indicated that there are certain scenarios like no guarantee of the other Activity being alive, I guess there are more probable and correct reasons as to why people suggest going for intents and shared preferences.

Community
  • 1
  • 1
Kazekage Gaara
  • 14,972
  • 14
  • 61
  • 108

10 Answers10

12

The answer to your question is two fold:

  • For the meta aspect, which belongs on meta SO anyway, many newbie programmers see Android, want to write apps, and suck at Java.
  • For the other quesiton, typically using a getter and setter won't work, because you can't pass objects between Activities in a straightforward way. While you can technically do this with a Parcelable, it's not recommended, and the better way is to use an intent to pass data between application components.
  • Another point this highlights is that Android apps should keep a minimal amount of state inside components. I think this has been a big success of Android. If you look at apps out there, there is on average a lot less global state than typical programs written in java. The programs are also smaller, which is to be expected, but the fact that an atomic activity can represent the state of a single screen, and the fact that any single screen won't typically be persisting that much state across the entire app, leads to an good logical separation between app components.
Kristopher Micinski
  • 7,572
  • 3
  • 29
  • 34
  • It's not just you, someone is downvoting all the answers. – Malcolm Jun 12 '12 at 14:00
  • @KristopherMicinski thanks for the answer,that too in a quick time, and that too when the question was attacked by moderators and down voters. Clears a lot of things for me. Thanks again. :-) – Kazekage Gaara Jun 12 '12 at 17:56
5

The simple answer is because the Activity life cycle is controlled by the Android OS. Activities are unlike normal classes which are instantiated by the user code and are guaranteed to be available till they are no longer referenced.

Rajesh
  • 15,724
  • 7
  • 46
  • 95
4

I believe the reason that Activities dont have have getters and setter is related to the lifecycle of an Activity. You really shouldn't guarantee that other Activities are alive since if they are not onScreen the system can clean them up at any give time.

However, to follow your pattern, you extend Application and use getters and setters for that. How to declare global variables in Android?

Community
  • 1
  • 1
Frank Sposaro
  • 8,511
  • 4
  • 43
  • 64
  • 1
    Perhaps, but using global variables is a bad (and uninformed) idea in Android in general, and sacrificing Android style for a preference of java style seems quite contrived. – Kristopher Micinski Jun 12 '12 at 13:53
  • Oh I agree. You learn that globals are bad in an intro programming class. I was just trying to show him how one would use that pattern. Im my experience extending Application everything usually ends up in there and it becomes very messy rather quickly. Good comment. Thanks. – Frank Sposaro Jun 12 '12 at 13:56
3

Mainly because the whole process of sending an intent is not that simple. An intent can travel through the system, between processes etc... in short the object you created is not the same object that is received at the end (this can be proven if trying to extend an intent class, send it to another activity and try to cast it back to your extended class on the other end, its simply not the same object).

Now i also really hate this, thats why i have created some base classes that help me work with intents (i call them BundleWrappers) which would work something like this:

you create a POJO with getters/setters, you fill that object and use it however you like,

then when the time comes just serialize into a bunle and deserialize it into the same object on the other end.

then you will have the same object with getters and setters in the other activity as well.

The main reason intents suck is that you have to find a way to keep track of all the keys for the extras, and the additional implementation for serializing the bundle.

still even with my method its not easy to use intents but it is the best i have found so far in terms of performance and object organization.

public abstract class BundleWrapper implements Parcelable {

    protected static final String KEY_PARCELABLE = "key_parcelable";

    public static final String TAG = BundleWrapper.class.getSimpleName();

    public BundleWrapper() {
        super();
    }

    abstract Parcelable getParcelable();

    public Bundle toBundle(){
        final Bundle bundle = new Bundle();
        Parcelable parcelable = getParcelable();
        if (parcelable != null) {
            bundle.setClassLoader(parcelable.getClass().getClassLoader());
            bundle.putParcelable(KEY_PARCELABLE, parcelable);
        }
        return bundle;
    }

    public static Object fromBundle(final Intent intent) {
        return fromBundle(intent.getExtras());
    }

    public static Object fromBundle(final Bundle bundle) {
        if (bundle != null && bundle.containsKey(KEY_PARCELABLE)) {
            bundle.setClassLoader(BundleWrapper.class.getClassLoader());
            return bundle.getParcelable(KEY_PARCELABLE);
        }
        return null;
    }

}

Here is my base class, for using it you simply extend it and implement parcelable(the retarded part of the process :):

public class WebViewFragmentBundle extends BundleWrapper implements Parcelable {

    public static final String TAG = WebViewFragmentBundle.class.getSimpleName();

    private String url;
    public WebViewFragmentBundle() {
        super();
    }

    public WebViewFragmentBundle(Parcel source) {
        this.url = source.readString();
    }

    public String getUrl() {
        return url;
    }


    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    Parcelable getParcelable() {
        return this;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(url);
    }

    public static final Parcelable.Creator<WebViewFragmentBundle> CREATOR = new Parcelable.Creator<WebViewFragmentBundle>() {
        @Override
        public WebViewFragmentBundle createFromParcel(Parcel source) {
            return new WebViewFragmentBundle(source);
        }

        @Override
        public WebViewFragmentBundle[] newArray(int size) {
            return new WebViewFragmentBundle[size];
        }
    };


}

and for a use case:

public static void launchAugmentedRealityActivityForResult(final Activity context, WebViewFragmentBundle wrapper) {
        final Intent intent = new Intent(context, Augmented.class);
        intent.putExtras(wrapper.toBundle());
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        context.startActivityForResult(intent, AUGMENTED_RESULT_CODE);
    }

and cast it on the other end like:

(WebViewFragmentBundle)BundleWrapper.fromBundle(getIntent());
DArkO
  • 15,880
  • 12
  • 60
  • 88
2

You are right when it comes to basic Class structure. However if, you consider activity lifecycle and memory management keeping whole activity alive for accessing small amount of data is not logical.

Gökhan Barış Aker
  • 4,445
  • 5
  • 25
  • 35
2

The situation is slightly more complicated than you suggest. If you are simply writing classes that exist inside the life cycle of an activity, and you want them to access certain members of the activity, than you could easily use getters and typical java paradigms.

That being said, Android does not contain some natural mechanism to access the instance of another Activity. This is probably very intentional. Activities are meant to operate in a distinct fashion. The closest comparison is that each Activity is meant to be like a page on a website (so referencing another instance of a page wouldn't make much sense).

Justin Breitfeller
  • 13,737
  • 4
  • 39
  • 47
2

I don't think this is somehow unique to Android. Any relatively sophisticated Java-based framework has higher-level 'rules' like this.

  • Java's Swing or AWT restrict you from calling certain methods from certain threads.
  • Java ME works very much like Android in this respect.
  • In Java EE -- forget about trying to share anything across Servlets or EJBs with static members. They aren't even on the same machine, maybe.

To directly answer your question: you can't simply access objects "freely" in the same way you might in a simple Java program since some assumptions that that depends on break down, namely, that these objects are even in the same ClassLoader.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
Sean Owen
  • 66,182
  • 23
  • 141
  • 173
2

This would work perfectly if the program could control when activities are created and destroyed. But the problem is that they are managed by the OS.

You can store a reference to another activity. Only what will happen if it gets destroyed or recreated? You will have a reference to the class instance which is longer relevant with no reliable means to detect this case.

Shared preferences and intents are used in these cases because they are state-independent. No matter what happens to any activity, preferences will always be available in the state they were. Intent is also an object which exists by itself and won't get stale.

Malcolm
  • 41,014
  • 11
  • 68
  • 91
2

To use your example, the first problem that you have is how to pass a reference to an instance of OneClass to SomeOtherClass. SomeOtherClass would need a reference to the instance of OneClass in order to call oneClass.getVariable(). There is no easy way to do this because when an Activity starts another Activity it does it by calling startActivity() and passing an Intent. That's the mechanism that you have readily available for passing parameters to an Activity when you start it, which is why you should probably use it.

Following on your example, another choice would be to use a static (class) variable to hold the data you want to pass between activities. So you could do something like this:

class OneClass extends Activity {
    private static int a;

    public static int getA() {
        return a;
    }

    ..
    // some changes to a
    ..
}

class SomeOtherClass extends Activity {
    ..
    // trying to access a here
    int myA = OneClass.getA();
}

However, this pretty much assumes that there will be only one instance of OneClass and that's where it all breaks down IMHO. In Android, activities get created and destroyed all over the place. There can be several instances of an activity at any given time and you never know how many you have or which one is the currently active one. That makes static variables inside Activity classes difficult to get right. For passing data between activities I would use the Extras in Intent, because it is clear that you are passing data when starting an Activity. It is self-documenting.

On the other hand, if you have data that is really global for your whole application, then I would just use static (class) variables in some class that is accessible from all classes directly. Some people subclass Application to do this, but the documentation indicates that you don't have to and in general you don't have to. You can just do something like this:

public class Globals {
    public static int a; // Publicly available, don't need getter
}

Then, any class can just store something in Globals.a or access it. You don't need to subclass Application for this.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
0

Solution: An Activity is an application component, it has its life cycle and back stack unlike classes. Though we can pass objects through parceable and serialization but it is not recommended. We can pass the object through bundle in intent object or use shared preferences to access the object. Using getter wouldn't be a good idea. Or you can create a separate Constant Class and define static varaible there and then can access it.

Amey Haldankar
  • 2,223
  • 1
  • 24
  • 22