1. ホーム
  2. android

[解決済み] Androidでグローバル変数を宣言するには?

2022-03-16 12:19:49

質問

ログインが必要なアプリケーションを作成しています。メインとログインのアクティビティを作成しました。

メインアクティビティでは onCreate メソッドに以下の条件を追加しました。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

onActivityResult メソッドは、ログインフォームが終了したときに実行されます。

@Override
public void onActivityResult(int requestCode,
                             int resultCode,
                             Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode)
    {
        case(SHOW_SUBACTICITY_LOGIN):
        {
            if(resultCode == Activity.RESULT_OK)
            {

                strSessionString = data.getStringExtra(Login.SESSIONSTRING);
                connectionAvailable = true;
                strUsername = data.getStringExtra(Login.USERNAME);
            }
        }
    }

問題は、ログインフォームが時々2回表示されることです( login() メソッドが2回呼び出されます)、さらに携帯電話のキーボードがスライドすると、ログインフォームが再び表示されます。 strSessionString .

既に認証に成功した後にログインフォームが表示されないようにするために、グローバル変数を設定する方法をご存知の方はいらっしゃいますか?

解決方法は?

私がこの回答を書いたのは、Androidが比較的新しく、Androidの開発において確立されていない部分が多くあった2009年にさかのぼります。この投稿の最後に長い補遺を加え、いくつかの批判に対処し、アプリケーションのサブクラス化ではなくシングルトンの使用に対する私の哲学的な意見の相違を詳述しています。ご自分の責任でお読みください。

オリジナルの回答です。

あなたが遭遇しているより一般的な問題は、複数のアクティビティとアプリケーションのすべての部分にわたって状態を保存する方法です。静的変数(例えばシングルトン)は、これを実現するための一般的なJavaの方法です。しかし、私はAndroidでよりエレガントな方法は、アプリケーションのコンテキストに状態を関連付けることであることを発見しました。

ご存知のように、各アクティビティは広義の実行環境に関する情報であるコンテキスト(Context)でもあります。アプリケーションにもコンテキストがあり、Androidはそれがアプリケーション全体で単一のインスタンスとして存在することを保証しています。

のサブクラスを作成することです。 android.app.Application そして、そのクラスをマニフェストのアプリケーションタグで指定します。これで、Android は自動的にそのクラスのインスタンスを作成し、アプリケーション全体で利用できるようになります。このクラスには、任意の context を使用しています。 Context.getApplicationContext() メソッド( Activity もメソッドを提供します。 getApplication() これは全く同じ効果をもたらします)。以下は、極めて単純化した例で、注意点もあります。

class MyApp extends Application {

  private String myState;

  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

これは、静的変数やシングルトンを使用するのと本質的に同じ効果をもたらしますが、既存のAndroidフレームワークに非常にうまく統合されています。この方法は、プロセス間で動作しないことに注意してください(アプリが複数のプロセスを持つまれなものの1つである場合)。

上の例で注意すべきことは、もし代わりに次のようなことを行っていたとします。

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

さて、この遅い初期化(ディスクを叩く、ネットワークを叩く、ブロックするものなど)は、Applicationがインスタンス化されるたびに実行されることになります と思うかもしれませんが、まあ、この処理は1回だけだし、どうせコストを払うことになるんでしょう? 例えば、Dianne Hackborn が後述するように、バックグラウンドのブロードキャストイベントを処理するためだけにプロセスをインスタンス化することは十分可能です。もしブロードキャスト処理がこの状態を必要としないのであれば、一連の複雑で低速な処理を無駄にしている可能性があります。遅延インスタンス化は、このゲームの名前です。以下は、Applicationを使用する少し複雑な方法で、最も単純な用途以外ではより理にかなっています。

class MyApp extends Application {

  private MyStateManager myStateManager = new MyStateManager();

  public MyStateManager getStateManager(){
    return myStateManager ;
  }
}

class MyStateManager {

  MyStateManager() {
    /* this should be fast */
  }

  String getState() {
    /* if necessary, perform blocking calls here */
    /* make sure to deal with any multithreading/synchronicity issues */

    ...

    return state;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
    String state = stateManager.getState();
    ...
  }
}

私は、よりエレガントなソリューションとして、シングルトンよりもアプリケーションのサブクラス化を好みますが、開発者には、アプリケーションのサブクラスに状態を関連付けることによるパフォーマンスやマルチスレッドの影響を全く考えずに、本当に必要であればシングルトンを使用してほしいと考えています。

注1: また、anticafe さんのコメントにあるように、Application override をアプリケーションに正しく関連付けるには、マニフェストファイルにタグが必要です。詳細については、Android ドキュメントを参照してください。例を挙げます。

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

注2. user608578さんから、ネイティブ・オブジェクトのライフサイクルを管理する上でどのように機能するのか、という質問がありました。私はAndroidでネイティブコードを使用するスピードに少しも達していませんし、それが私のソリューションとどのように相互作用するかを答える資格もありません。もし誰かがこれに対する答えを知っていたら、その人の名前をクレジットして、この投稿に情報を載せて、最大限の可視性を確保したいと思います。

追記

何人かの方が指摘されているように、これは ではなく に対する解決策です。 しつこい の状態であることを、最初の回答でもっと強調すべきだったかもしれません。つまり、これはユーザーやその他の情報を保存するためのソリューションではなく、アプリケーションのライフタイムを超えて持続させることを意図しています。したがって、ディスクに永続化する必要があるものは、アプリケーションのサブクラスを通じて保存されるべきではないので、アプリケーションがいつでも終了することなどに関連する以下のほとんどの批判は無意味だと考えています。これは、一時的で簡単に再作成できるアプリケーションの状態(例えば、ユーザーがログインしているかどうか)や、シングルインスタンスのコンポーネント(例えば、アプリケーションネットワークマネージャ)を保存するためのソリューションであることを意図しています( NOT singleton!)的な性質がある。

Dayermanは、興味深いことを指摘してくれました。 レト・マイヤー、ダイアン・ハックボーンとの会話 の中で、アプリケーションのサブクラスの使用は推奨されず、シングルトンパターンが支持されています。Somatikも以前このようなことを指摘していたが、私はその時見ていなかった。RetoとDianneはAndroidプラットフォームのメンテナンスに携わっているので、彼らのアドバイスを無視することは誠意を持ってお勧めできません。彼らの言うことは絶対です。アプリケーションのサブクラスよりもシングルトンを優先するという意見には、反対です。私の反対意見には、次のサイトで説明されているようなコンセプトが使われています。 StackExchangeによるSingletonデザインパターンについての解説です。 この回答で用語を定義する必要がないようにするためです。この回答を続ける前に、このリンクに目を通すことを強くお勧めします。一点一点です。

Dianneは次のように述べています: "Applicationからサブクラス化する理由はありません。シングルトンを作るのと変わらない..." この最初の主張は間違っています。これには2つの主な理由があります。1) Applicationクラスは、アプリケーション開発者にとって、より良いライフタイム保証を提供します。シングルトンはアプリケーションのライフタイムと明示的に結びついているわけではありません(事実上そうなっていますが)。しかし、これこそAndroid APIが提供すべき契約であり、関連データの寿命を最小限に抑えることで、Androidシステムにもより多くの柔軟性を提供するものだと私は主張します。2) Applicationクラスは、アプリケーション開発者に状態のシングルインスタンスホルダーを提供しますが、これは状態のシングルトンホルダーとは大きく異なります。この違いは、上記のSingletonの説明のリンクを参照してください。

Dianneはこう続けます。「...ただ、将来的に、独立したアプリケーションロジックであるべきものが、アプリケーションオブジェクトがこのように大きく絡み合っていることに気づき、後悔する可能性が高いです。Dianeの主張はどれも、Singletonを使うことがApplication subclassよりも良いという理由を示しておらず、彼女が立証しようとしているのは、Singletonを使うことはApplication subclassよりも悪くないということですが、私はこれは誤りだと考えています。

これは、アプリケーションのサブクラスを使ってオンデマンドで初期化できない理由がない、という事実を無視しています。ここでも違いはありません。

Dianneは次のように締めくくりました。「フレームワーク自体には、読み込まれたリソースのキャッシュやオブジェクトのプールなど、アプリのために維持する小さな共有データ用のシングルトンが大量に用意されています。私は、シングルトンを使用するとうまくいかないとか、正当な代替手段でないと主張しているのではありません。シングルトンでは、アプリケーションのサブクラスを使用する場合ほど、Android システムとの強い契約は提供されません。さらに、シングルトンを使用すると、一般的に柔軟性のない設計になり、簡単に変更できず、将来的に多くの問題を引き起こすと主張しているのです。IMHOは、Android APIが開発者のアプリケーションに提供する強力な契約は、Androidを使ったプログラミングの最も魅力的で楽しい側面の1つであり、Androidプラットフォームを今日の成功に導いた初期の開発者の採用につながったと思っています。Singletonsの使用を提案することは、暗黙のうちに強力なAPIコントラクトから遠ざかっており、私の意見では、Androidフレームワークを弱体化させています。

Dianneが以下のコメントで、アプリケーションのサブクラスを使用する際のもう1つのマイナス面について触れています。これは非常に正しいことで、私はこの回答を編集して、ここでperfを考慮することの重要性と、アプリケーションのサブクラスを使用している場合に正しいアプローチをとることを強調しました。Dianneが述べているように、アプリケーションが複数のプロセスで動作している場合、プロセスがロードされるたびにApplicationクラスがインスタンス化されることを覚えておくことが重要です(バックグラウンドブロードキャストイベントのためだけにプロセスがロードされている場合でも、一度に複数回ロードされる可能性があります!)。したがって、アプリケーションクラスは、処理を行う場所としてではなく、アプリケーションの共有コンポーネントへのポインタのリポジトリとして使用することが重要です!

StackExchangeのリンクから引用した、シングルトンの欠点を以下に列挙します。

  • 抽象クラスやインターフェースクラスが使用できない。
  • サブクラス化ができない
  • アプリケーション全体のカップリングが高い(修正が難しい)。
  • テストが難しい(ユニットテストでのフェイク/モックができない)。
  • ミュータブルステートの場合、並列化が難しい(大規模なロックが必要)。

と自分なりに追加しています。

  • 不明確で管理しにくい生涯契約は、Android(または他のほとんどの)開発には不向きです。