12

毎日、SOには次のタイプの質問がたくさんあります。

別の変数から変数を取得するにはどうすればよいActivityですか?

回答は通常、SharedPreferencesまたはを使用することをお勧めしますIntent.putExtra()

私にとって、getterメソッドとは、別のクラスの変数にアクセスするためのアプローチです。結局のところ、Activity検討中のはクラスであり、その変数はクラスメンバーです。

SharedPreferencesやIntentextrasなどのアプローチよりもgetterメソッドが優先されないのはなぜですか?

私は、アクティビティ間で変数にアクセスする必要がある単純な状況について話しています。たとえば、次のようなものです。

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

あなたの質問に対する答えは 2 つあります。

  • とにかくメタ SO に属するメタの側面については、多くの初心者プログラマーが Android を見て、アプリを書きたがり、Java が苦手です。
  • 他の質問については、アクティビティ間でオブジェクトを簡単な方法で渡すことができないため、通常、ゲッターとセッターを使用しても機能しません。技術的には Parcelable を使用してこれを行うことはできますが、推奨されません。より良い方法は、インテントを使用してアプリケーション コンポーネント間でデータを渡すことです。
  • ここで強調するもう 1 つのポイントは、Android アプリはコンポーネント内に最小限の状態を保持する必要があるということです。これは Android の大きな成功だと思います。世の中に出回っているアプリを見ると、Java で書かれた典型的なプログラムよりも平均してグローバル状態がはるかに少ないです。プログラムも小さくなっていますが、これは当然のことですが、アトミック アクティビティが 1 つの画面の状態を表すことができるという事実と、1 つの画面が通常、アプリ全体でそれほど多くの状態を保持するわけではないという事実は、アプリ コンポーネント間の適切な論理的分離に。
于 2012-06-12T13:52:57.000 に答える
5

簡単な答えは、Activityライフサイクルが Android OS によって制御されているためです。アクティビティは、ユーザー コードによってインスタンス化され、参照されなくなるまで使用できることが保証されている通常のクラスとは異なります。

于 2012-06-12T13:49:05.557 に答える
4

アクティビティにゲッターとセッターがない理由は、アクティビティのライフサイクルに関連していると思います。他のアクティビティがオンスクリーンでない場合、システムはいつでもそれらをクリーンアップできるため、他のアクティビティが生きていることを保証するべきではありません。

ただし、パターンに従うには、アプリケーションを拡張し、そのためにゲッターとセッターを使用します。Androidでグローバル変数を宣言するには?

于 2012-06-12T13:49:45.217 に答える
3

主な理由は、インテントを送信するプロセス全体がそれほど単純ではないためです。インテントは、システム内、プロセス間などで移動できます。つまり、作成したオブジェクトは、最後に受信したオブジェクトと同じではありません(これは、インテントクラスを拡張して別のアクティビティに送信し、反対側の拡張クラスにキャストして戻してみてください。同じオブジェクトではありません)。

今、私もこれが本当に嫌いです。そのため、次のように機能するインテント(BundleWrappersと呼びます)を操作するのに役立つ基本クラスをいくつか作成しました。

ゲッター/セッターを使用してPOJOを作成し、そのオブジェクトを埋めて、好きなように使用します。

次に、時が来たら、バンドルにシリアル化し、もう一方の端で同じオブジェクトに逆シリアル化します。

そうすれば、他のアクティビティでもゲッターとセッターと同じオブジェクトを使用できます。

インテントがうまくいかない主な理由は、エクストラのすべてのキーを追跡する方法と、バンドルをシリアル化するための追加の実装を見つける必要があることです。

それでも私の方法でも、インテントを使用するのは簡単ではありませんが、パフォーマンスとオブジェクト編成の点でこれまでに見つけた中で最高です。

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

あなたの例を使用するために、あなたが持っている最初の問題は、へのインスタンスへの参照をどのように渡すかOneClassですSomeOtherClass。を呼び出すにSomeOtherClassは、のインスタンスへの参照が必要になります。アクティビティが別のアクティビティを開始すると、インテントを呼び出して渡すことでそれを実行するため、これを行う簡単な方法はありません。これは、アクティビティを開始するときにパラメータをアクティビティに渡すためにすぐに利用できるメカニズムです。そのため、おそらくそれを使用する必要があります。OneClassoneClass.getVariable()startActivity()

あなたの例に続いて、別の選択肢は、静的(クラス)変数を使用して、アクティビティ間で渡したいデータを保持することです。したがって、次のようなことができます。

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

ただし、これは、のインスタンスが1つしかないことをほぼ想定しておりOneClass、ここですべてがIMHOを分解します。Androidでは、アクティビティはいたるところに作成および破棄されます。いつでもアクティビティのインスタンスがいくつか存在する可能性があり、自分がいくつ持っているか、または現在アクティブなものがどれであるかはわかりません。そのため、Activityクラス内の静的変数を正しく取得するのは困難です。アクティビティ間でデータを渡す場合は、アクティビティの開始時にデータを渡すことが明らかであるため、IntentのExtrasを使用します。自己文書化です。

一方、アプリケーション全体で本当にグローバルなデータがある場合は、すべてのクラスから直接アクセスできるクラスで静的(クラス)変数を使用します。これを行うためにサブクラス化する人もApplicationいますが、ドキュメントには、そうする必要はなく、一般的にはそうする必要がないことが示されています。あなたはちょうどこのようなことをすることができます:

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

そうすれば、どのクラスも何かを保存しGlobals.aたり、それにアクセスしたりできます。このためにサブクラス化する必要はありませんApplication

于 2012-06-12T15:37:41.017 に答える
2

これはどういうわけかAndroidに固有のものではないと思います。比較的洗練されたJavaベースのフレームワークには、このような高レベルの「ルール」があります。

  • JavaのSwingまたはAWTは、特定のスレッドから特定のメソッドを呼び出すことを制限します。
  • Java MEは、この点でAndroidと非常によく似ています。
  • JavaEEの場合-サーブレットまたはEJB間でstaticメンバーと何かを共有しようとすることを忘れてください。たぶん、それらは同じマシン上にさえありません。

質問に直接答えるには、単純なJavaプログラムの場合と同じように、オブジェクトに「自由に」アクセスすることはできません。これは、ブレークダウンに依存するいくつかの仮定、つまり、これらのオブジェクトが同じであるためClassLoaderです。

于 2012-06-12T13:54:43.030 に答える
2

これは、アクティビティがいつ作成および破棄されるかをプログラムが制御できれば、完全に機能します。しかし問題は、それらがOSによって管理されていることです。

別のアクティビティへの参照を保存できます。それが破壊または再作成された場合にのみ何が起こりますか?このケースを検出するための信頼できる手段がなく、より関連性の高いクラスインスタンスへの参照があります。

これらの場合、状態に依存しないため、共有の設定とインテントが使用されます。アクティビティに何が起こっても、設定は常に元の状態で利用できます。インテントは、それ自体で存在し、古くなることのないオブジェクトでもあります。

于 2012-06-12T13:55:26.443 に答える
2

基本的なクラス構造に関しては、あなたは正しいです。ただし、アクティビティのライフサイクルとメモリ管理を考慮して、少量のデータにアクセスするためにアクティビティ全体を維持することは論理的ではありません。

于 2012-06-12T13:49:54.793 に答える
2

状況は、あなたが示唆するよりも少し複雑です。アクティビティのライフサイクル内に存在するクラスを単純に作成し、そのクラスにアクティビティの特定のメンバーにアクセスさせたい場合は、getter と典型的な Java パラダイムを簡単に使用できます。

そうは言っても、Android には、別のアクティビティのインスタンスにアクセスするための自然なメカニズムが含まれていません。これはおそらく非常に意図的なものです。アクティビティは、明確な方法で動作することを意図しています。最も近い比較は、各アクティビティが Web サイト上のページのようなものであることを意味します (そのため、ページの別のインスタンスを参照することはあまり意味がありません)。

于 2012-06-12T13:54:22.077 に答える
0

解決策: アクティビティはアプリケーション コンポーネントであり、クラスとは異なり、ライフ サイクルとバック スタックがあります。parceable とシリアライゼーションを介してオブジェクトを渡すことはできますが、お勧めしません。インテント オブジェクトのバンドルを介してオブジェクトを渡すか、共有設定を使用してオブジェクトにアクセスできます。ゲッターを使用するのは良い考えではありません。または、別の定数クラスを作成し、そこで静的変数を定義してからアクセスできます。

于 2014-12-18T09:43:22.777 に答える