在活动之间访问变量时,为什么没有首选的getter?

时间:2012-06-12 13:44:59

标签: java android

每天在SO上有以下type的许多问题:

  

如何从另一个Activity获取变量?

答案通常建议使用SharedPreferencesIntent.putExtra()

对我来说,getter方法是从另一个类访问变量的方法。毕竟,正在考虑的Activity是一个类,它的变量是类成员。

为什么getter方法不适用于SharedPreferences或Intent extras等方法?

我在谈论需要在活动之间访问变量的简单情况,例如:

class OneClass extends Activity {
    int a;

    ..
    // some changes to a
    ..
}

然后在另一个班级(Activity):

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

getter方法在这里是否是正确的方法?

再一次 - 我不是在谈论这些事情实际上是正确的方法。 SharedPreferences用于持久存储少量数据extras,如文档所述:这可用于向组件提供扩展信息。例如,如果我们有一个发送电子邮件的动作,我们还可以在这里包含额外的数据来提供主题,正文等。


由于某些答案可能表明存在某些情况,例如无法保证其他Activity还活着,我想有更多可能和正确的理由说明为什么人们建议进行意图和共享偏好

10 个答案:

答案 0 :(得分:12)

你的问题的答案有两个:

  • 对于属于meta SO的元方面,许多新手程序员都会看到Android,想要编写应用程序,并且习惯于Java。
  • 对于另一个问题,通常使用getter和setter将无法工作,因为您无法以直接的方式在Activities之间传递对象。虽然您可以使用Parcelable在技术上执行此操作,但不建议这样做,更好的方法是使用intent在应用程序组件之间传递数据。
  • 这一点强调的另一点是Android应用程序应该在组件内部保持最少量的状态。我认为这是Android的一大成功。如果你看看那里的应用程序,平均比用java编写的典型程序少得多的全局状态。程序也较小,这是预期的,但事实上原子活动可以代表单个屏幕的状态,并且任何单个屏幕通常不会在整个应用程序中保持那么多状态的事实,导致在应用程序组件之间实现良好的逻辑分离。

答案 1 :(得分:5)

简单的答案是因为Activity生命周期由Android操作系统控制。活动与普通类不同,普通类由用户代码实例化,并保证在不再引用之前可用。

答案 2 :(得分:4)

我认为Activities没有getter和setter的原因与Activity的生命周期有关。你真的不应该保证其他活动是活着的,因为如果它们不在屏幕上,系统可以在任何给定时间清理它们。

但是,要遵循您的模式,您可以扩展应用程序并使用getter和setter。 How to declare global variables in Android?

答案 3 :(得分:3)

主要是因为发送意图的整个过程并不那么简单。意图可以通过系统,进程之间等...简而言之,您创建的对象与最终收到的对象不同(如果尝试扩展意图类,将其发送到另一个活动,则可以证明这一点尝试将它强制转换回另一端的扩展类,它只是不同的对象。)

现在我也非常讨厌这个,这就是为什么我创建了一些基类来帮助我处理意图(我称之为BundleWrappers),它可以像这样工作:

你用getter / setter创建一个POJO, 你填写那个对象并随意使用它,

然后当时间到来时,只需序列化为一个bunle并将其反序列化为另一端的同一个对象。

然后你将在另一个活动中拥有与getter和setter相同的对象。

意图很糟糕的主要原因是你必须找到一种方法来跟踪附加内容的所有密钥,以及序列化捆绑包的附加实现。

仍然使用我的方法它不容易使用意图,但它是迄今为止在性能和对象组织方面发现的最好。

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;
    }

}

这是我的基类, 使用它你只需扩展它并实现parcelable(过程的延迟部分:):

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];
        }
    };


}

和用例:

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);
    }

并将其投射到另一端,如:

(WebViewFragmentBundle)BundleWrapper.fromBundle(getIntent());

答案 4 :(得分:2)

在基本的类结构方面,你是对的。但是,如果您认为活动生命周期和内存管理使整个活动保持活动以访问少量数据是不合逻辑的。

答案 5 :(得分:2)

情况比你建议的要复杂一些。如果您只是编写存在于活动生命周期内的类,并且您希望它们访问活动的某些成员,那么您可以轻松使用getter和典型的Java范例。

话虽如此,Android并没有包含一些访问另一个Activity实例的自然机制。这可能是非常有意的。活动旨在以独特的方式运作。最接近的比较是每个Activity都像网站上的页面一样(因此引用页面的另一个实例没有多大意义)。

答案 6 :(得分:2)

我不认为这对Android来说是独一无二的。任何相对复杂的基于Java的框架都有这样的更高级别的“规则”。

  • Java的Swing或AWT限制您从某些线程调用某些方法。
  • 在这方面,Java ME与Android非常相似。
  • 在Java EE中 - 忘记尝试与static成员在Servlet或EJB之间共享任何内容。他们甚至不在同一台机器上。

直接回答你的问题:你不能像在简单的Java程序中那样“自由地”访问对象,因为一些假设依赖于分解,即这些对象甚至是相同的ClassLoader

答案 7 :(得分:2)

如果程序可以控制何时创建和销毁活动,这将完美地工作。但问题是它们是由操作系统管理的。

您可以存储对其他活动的引用。只有它被破坏或重建后会发生什么?您将获得对类实例的引用,该实例与较长时间相关,但没有可靠的方法来检测此情况。

在这些情况下使用共享首选项和意图,因为它们独立于状态。无论任何活动发生什么,偏好都将始终以他们所处的状态提供。 Intent也是一个独立存在的对象,不会过时。

答案 8 :(得分:2)

要使用您的示例,您遇到的第一个问题是如何将对OneClass的实例的引用传递给SomeOtherClassSomeOtherClass需要引用OneClass的实例才能调用oneClass.getVariable()。没有简单的方法可以做到这一点,因为当一个Activity启动另一个Activity时,它通过调用startActivity()并传递一个Intent来完成它。这是您在启动时将参数传递给Activity的机制,这就是您应该使用它的原因。

在您的示例之后,另一种选择是使用静态(类)变量来保存您希望在活动之间传递的数据。所以你可以这样做:

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();
}

然而,这几乎假设只有OneClass的一个实例,而这就是它摧毁恕我直言的地方。在Android中,活动在整个地方被创建和销毁。在任何给定时间都可能存在多个活动实例,您永远不会知道您拥有多少活动或哪个是当前活动活动。这使得Activity类中的静态变量很难正确。为了在活动之间传递数据,我将使用Intent in Intent,因为很明显你在启动Activity时传递数据。它是自我记录的。

另一方面,如果你的整个应用程序的数据真的是全局的,那么我只会在某些类中使用静态(类)变量,这些变量可以直接从所有类访问。有些人将Application子类化为此,但文档表明您没有,并且通常您不必这样做。你可以这样做:

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

然后,任何班级都可以在Globals.a中存储或访问它。您不需要为此Application创建子类。

答案 9 :(得分:0)

解决方案: Activity是一个应用程序组件,它具有与类不同的生命周期和后向堆栈。 虽然我们可以通过parceable和序列化传递对象,但不建议这样做。我们可以通过intent对象中的bundle传递对象,或者使用共享首选项来访问对象。使用getter不是一个好主意。 或者您可以创建一个单独的常量类并在那里定义静态变量,然后可以访问它。