12

每天都有很多关于SO的以下类型的问题:

如何从另一个获取变量Activity

答案通常建议使用SharedPreferencesor Intent.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人还活着,我想人们为什么建议追求意图和共享偏好有更多可能和正确的原因。

4

10 回答 10

12

你的问题的答案有两个:

  • 对于无论如何都属于 meta SO 的 meta 方面,许多新手程序员看到了 Android,想要编写应用程序,并且对 Java 感到厌烦。
  • 对于另一个问题,通常使用 getter 和 setter 是行不通的,因为您不能以直接的方式在活动之间传递对象。虽然您可以在技术上使用 Parcelable 执行此操作,但不建议这样做,更好的方法是使用意图在应用程序组件之间传递数据。
  • 这里强调的另一点是,Android 应用程序应该在组件内部保持最少的状态。我认为这是Android的巨大成功。如果您查看那里的应用程序,平均而言,全局状态比用 java 编写的典型程序要少得多。这些程序也更小,这是意料之中的,但是原子活动可以代表单个屏幕的状态,以及任何单个屏幕通常不会在整个应用程序中保持那么多状态的事实,导致应用程序组件之间的良好逻辑分离。
于 2012-06-12T13:52:57.000 回答
5

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

于 2012-06-12T13:49:05.557 回答
4

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

但是,要遵循您的模式,您可以扩展 Application 并为此使用 getter 和 setter。如何在Android中声明全局变量?

于 2012-06-12T13:49:45.217 回答
3

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

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

您使用 getter/setter 创建一个 POJO,填充该对象并随心所欲地使用它,

然后当时机成熟时,只需将其序列化为一个包并将其反序列化为另一端的同一对象。

那么您将在其他活动中拥有与 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());
于 2012-06-12T14:02:15.277 回答
2

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

于 2012-06-12T13:49:54.793 回答
2

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

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

于 2012-06-12T13:54:22.077 回答
2

我不认为这在某种程度上是 Android 独有的。任何相对复杂的基于 Java 的框架都有像这样的更高级别的“规则”。

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

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

于 2012-06-12T13:54:43.030 回答
2

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

您可以存储对另一个活动的引用。只有当它被破坏或重建时会发生什么?您将获得对不再相关的类实例的引用,而没有可靠的方法来检测这种情况。

在这些情况下使用共享的偏好和意图,因为它们是独立于状态的。无论任何活动发生什么,偏好都将始终保持其原来的状态。Intent 也是一个独立存在且不会过时的对象。

于 2012-06-12T13:55:26.443 回答
2

要使用您的示例,您遇到的第一个问题是如何传递对 to 实例的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 类中的静态变量很难正确处理。为了在 Activity 之间传递数据,我会使用 Intent 中的 Extras,因为很明显,您在启动 Activity 时正在传递数据。它是自我记录的。

另一方面,如果您的数据对于整个应用程序来说确实是全局的,那么我只会在某个类中使用静态(类)变量,这些类可以直接从所有类访问。有些人子类Application来执行此操作,但文档表明您不必这样做,而且通常您不必这样做。你可以这样做:

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

然后,任何类都可以存储Globals.a或访问它。您不需要为此进行子类化Application

于 2012-06-12T15:37:41.017 回答
0

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

于 2014-12-18T09:43:22.777 回答