Androidでグローバル変数を宣言する方法は?


595

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

メインアクティビティ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。問題は変数です。

ユーザーが認証に成功した後にログインフォームが表示されないように、変数をグローバルに設定する方法を知っている人はいますか?


保存されたインスタンスの状態バンドル使用して活動状態を処理する方法についての良いチュートリアルquicktips.in/...を
ディーパックスワミ

回答:


954

私がこの答えを書いたのは、Androidが比較的新しい'09年であり、Android開発には確立されていない領域が数多くありました。この投稿の最後に長い付録を追加し、いくつかの批判に対処し、アプリケーションをサブクラス化するのではなく、シングルトンを使用する場合の哲学的な不一致を詳しく説明しました。自己責任でお読みください。

元の回答:

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

ご存じのとおり、各アクティビティはコンテキストでもあります。コンテキストは、広い意味での実行環境に関する情報です。アプリケーションにはコンテキストもあり、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;
  }
}

この遅い初期化(ディスクのヒット、ネットワークのヒット、ブロッキングなど)は、アプリケーションがインスタンス化されるたびに実行されます。あなたは、まあ、これはプロセスのために一度だけです、そして私はとにかく費用を支払わなければならないでしょう、そうですか?たとえば、ダイアンハックボーンが以下で言及しているように、バックグラウンドブロードキャストイベントを処理するためだけに、プロセスをインスタンス化することは完全に可能です。ブロードキャスト処理でこの状態が必要ない場合は、一連の複雑で低速な操作を何もせずに実行した可能性があります。遅延インスタンス化は、ここでのゲームの名前です。以下は、アプリケーションの使用法が少し複雑な方法で、最も単純な使用法以外の場合に適しています。

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:また、アンチカフェがコメントしたように、アプリケーションのオーバーライドをアプリケーションに正しく関連付けるためには、マニフェストファイルにタグが必要です。繰り返しますが、詳細についてはAndroidのドキュメントをご覧ください。例:

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

注2: user608578は、ネイティブオブジェクトのライフサイクルの管理でこれがどのように機能するかを以下で尋ねます。私は、Androidでネイティブコードを少しでも使用することにスピードが足りず、それが私のソリューションとどのように相互作用するかについて答える資格がありません。誰かがこれに対する答えを持っている場合、私はそれらを信用し、最大限の可視性のためにこの投稿に情報を入れていく所存です。

補遺:

一部の人々が指摘したように、これは永続的な状態の解決策ではありません。おそらく元の答えでもっと強調すべきだったでしょう。つまり、これは、アプリケーションのライフタイム全体で保持されることを意図したユーザーまたはその他の情報を保存するためのソリューションではありません。したがって、ディスクに永続化する必要があるものはすべてアプリケーションサブクラスを介して保存してはならないため、アプリケーションがいつでも強制終了されることなどについて、以下のほとんどの批判を検討します。これは、一時的で簡単に再作成できるアプリケーションの状態(ユーザーがログインしているかどうかなど)と、単一のインスタンス(アプリケーションネットワークマネージャーなど)であるコンポーネント(シングルトンではない!)を保存するためのソリューションです。

Dayermanは、Reto MeierとDianne Hackbornとの興味深い会話を指摘するのに十分親切でした。そこでは、アプリケーションのサブクラスの使用はシングルトンパターンを支持するために推奨されていません。ソマティックはまた、このようなことを以前に指摘しましたが、私は当時はそれを見ていませんでした。Androidプラットフォームの保守におけるRetoとDianneの役割のため、私は誠意をもって彼らのアドバイスを無視することを勧めることはできません。彼らが言うことは、行きます。アプリケーションのサブクラスよりもシングルトンを優先することに関して表明された意見には同意しません。私の意見の相違では、このStackExchangeのシングルトン設計パターンの説明で最もよく説明されている概念を利用します。、この回答で用語を定義する必要がないようにします。続行する前にリンクをざっと読むことを強くお勧めします。ポイントごと:

ダイアンは、「アプリケーションからサブクラス化する理由はありません。シングルトンを作成することと同じです...」この最初の主張は正しくありません。これには主に2つの理由があります。1)Applicationクラスは、アプリケーション開発者により良いライフタイム保証を提供します。アプリケーションの寿命があることが保証されています。シングルトンは、アプリケーションの存続期間に明示的に結び付けられているわけではありません(実際にはそうですが)。これはあなたの平均的なアプリケーション開発者にとっては問題ではないかもしれませんが、これはまさにAndroid APIが提供すべき契約のタイプであり、関連するライフタイムを最小化することにより、Androidシステムにより多くの柔軟性を提供すると私は主張しますデータ。2)Applicationクラスは、アプリケーション開発者に状態の単一インスタンスホルダーを提供します。これは、状態のシングルトンホルダーとは大きく異なります。違いのリストについては、上記のシングルトンの説明リンクを参照してください。

Dianne氏は続けます。「アプリケーションオブジェクトが、独立したアプリケーションロジックであるべきもののこの大きな絡み合った混乱になっていることがわかると、将来、後悔することになるでしょう。」これは間違いなく間違いではありませんが、アプリケーションサブクラスではなくシングルトンを選択する理由にはなりません。ダイアンの主張はどれも、シングルトンを使用することがアプリケーションサブクラスよりも優れているという理由を提供していません。彼女が確立しようとしているのは、シングルトンを使用することはアプリケーションサブクラスよりも悪くないということです。

彼女は続けます、「そしてこれはあなたがこれらのものを管理する方法にもっと自然につながります-オンデマンドでそれらを初期化します。」これは、Applicationサブクラスを使用してオンデマンドで初期化できない理由がないことを無視しています。再び違いはありません。

Dianneは「フレームワーク自体が、ロードされたリソースのキャッシュやオブジェクトのプールなど、アプリ用に維持しているすべての小さな共有データ用の膨大な量のシングルトンを備えています。シングルトンを使用してもうまくいかない、または正当な代替手段ではないことについては、私は主張していません。シングルトンは、アプリケーションサブクラスを使用するよりもAndroidシステムとの強い契約を提供しておらず、シングルトンを使用すると、一般に柔軟性のない設計を指し示すため、簡単に変更できず、将来多くの問題が発生すると私は主張しています。私見、Android APIが開発者アプリケーションに提供する強力な契約は、Androidでのプログラミングの最も魅力的で楽しい側面の1つであり、Androidプラットフォームを今日の成功へと導いた開発者の早期採用につながりました。

ダイアンも以下のようにコメントしており、アプリケーションサブクラスを使用することの追加のマイナス面について言及しているため、パフォーマンスの低いコードを書くことを奨励したり、簡単にすることができます。これは非常に真実であり、ここでperfを検討することの重要性を強調するためにこの回答を編集し、アプリケーションのサブクラス化を使用している場合は正しいアプローチをとります。ダイアンが述べているように、プロセスがバックグラウンドブロードキャストでのみロードされている場合でも、プロセスがロードされるたびにアプリケーションクラスがインスタンス化されることを覚えておくことが重要です(アプリケーションが複数のプロセスで実行されている場合、一度に複数回可能です!)。イベント。したがって、Applicationクラスを、処理を行う場所としてではなく、アプリケーションの共有コンポーネントへのポインタのリポジトリとして使用することが重要です。

以前のStackExchangeリンクから盗んだ次のシングルトンの欠点のリストを残しておきます。

  • 抽象クラスまたはインターフェースクラスを使用できない。
  • サブクラス化できない;
  • アプリケーション全体の高い結合(変更が難しい);
  • テストが難しい(単体テストでは偽造/模擬できません);
  • 変更可能な状態の場合は並列化が困難です(大規模なロックが必要)。

そして私自身のものを追加してください:

  • Android(またはその他のほとんど)の開発には適さない、不明確で管理できない生涯契約。

93
Soonilに感謝します。このような答えが、私がStack Overflowをとても気に入っている理由です。よくやった!
JohnnyLambada

5
「マニフェストのアプリケーションタグでそのクラスを指定する」方法を知りたくない人のために、この執筆時点で、この質問にはその方法を説明する2つの回答があります(android:nameを使用)。1つはebuprofenによるもの、もう1つはebuprofenによるものです。マイク・ブラウン。
Tyler Collier

9
Soonil、あなたの答えは正しいですが、<application android:name = "。MyApp" ... />をAndroidマニフェストファイルに追加する必要があることに気づきましたか?
アンチカフェ

12
もう一度繰り返しましょう。グローバルアプリケーションは使用しないでください。これは役に立たず、シングルトンよりもメリットがありません。また、プロセスの起動のパフォーマンスを損なうなど、積極的に有害な場合があります。アプリケーションが作成されているとき、あなたは自分のプロセスが何のために作成されているのか分かりません。必要に応じてシングルトンを遅延初期化することで、必要な作業のみを実行できます。たとえば、バックグラウンドイベントに関するブロードキャストを処理するためにプロセスが起動されている場合、UIが必要とするグローバル状態を初期化する理由はありません。
hackbod 2014

14
また、ここで明確にしましょう。シングルトンとグローバルではない別のアプローチのどちらかを実際に選択している状況について話しているとき、シングルトンに対するすべての議論は完全に有効です。シングルトンはグローバルであり、適用されるグローバルに関するすべての警告があります。ただし、アプリケーションもシングルトンです。アプリケーションのサブクラス化に切り替えることでこれらの問題を回避することはできません。アプリケーションはシングルトンとまったく同じですが(さらに悪いことです)、よりクリーンなことをしていると自分をだますことができます。しかし、そうではありません。
hackbod 14

153

このサブクラスを作成する

public class MyApp extends Application {
  String foo;
}

AndroidManifest.xmlにandroid:nameを追加します

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

1
それをありがとう。私はそれをマニフェストで宣言する方法を考えていました
誰か誰かどこか

3
これが機能するためには、「。」を削除する必要がありました。".MyApp"内
誰かどこか

3
メインアクティビティの後で宣言するだけです。そうしないと、インストール/デプロイされない可能性があります
sami

11
ただ言いたいのですが、これはすでにそこにあるMAINアプリケーションタグに入ります...これは2番目のものではありません:)難しい方法を学ぶ必要がありました。
bwoogie、2011年

java.lang.IllegalAccessException: access to class is not allowed
ラプター2014年

142

Soonilが提案したアプリケーションの状態を維持する方法は優れていますが、1つの弱点があります。OSがアプリケーションプロセス全体を強制終了する場合があります。これは、これに関するドキュメントです- プロセスとライフサイクル

ケースを考えてみましょう-誰かがあなたを呼んでいるのであなたのアプリはバックグラウンドになります(電話アプリは現在フォアグラウンドにあります)。この場合、&&は他のいくつかの条件の下で(上記のリンクを確認してください)、OSがApplicationサブクラスインスタンスを含むアプリケーションプロセスを強制終了する場合があります。その結果、状態は失われます。後でアプリケーションに戻ると、OSはアクティビティスタックとApplicationサブクラスインスタンスを復元しますが、myStateフィールドはになりますnull

私の知る限り、状態の安全性を保証する唯一の方法は、アプリケーションファイルにプライベートを使用するなど、状態の永続化を使用することですSharedPrefernces(最終的には内部ファイルシステムのアプリケーションファイルにプライベートを使用します)。


10
+1で持続しSharedPreferencesます。これは私がそれを行ったのを見た方法です。保存された状態の設定システムを乱用するのは奇妙だと思いますが、問題が用語の問題になるほどうまく機能します。
Cheezmeister

1
SharedPreferencesを使用してArhimedが説明する問題を解決する方法について、コードを投稿(または説明へのリンクを提供)してください
Someone Somewhere

2
設定、データベース、ファイルのシリアル化など。各アクティビティは、onSaveInstanceStateを使用する場合は状態を維持できますが、ユーザーがアクティビティからバックアウトし、履歴スタックから削除した場合、デバイスを強制的に閉じた場合、またはデバイスをオフにした場合は役に立ちません。
ダレンヒンデラー2011年

1
この動作は非常に煩わしいものです。アプリケーションのonTerminate()メソッドが呼び出されて状況をエレガントに処理できれば、それほど悪くはありません。
Dean Wild、

2
これは私の意見では正しい答えです。アクティビティ全体に存在する同じアプリケーションインスタンスに依存するのはバグです。私の経験では、Androidがバックグラウンドで完全に分解してプロセス全体を再作成することは非常に一般的です。バックグラウンド化されているということは、単にカメラインテント、ブラウザインテントを起動するか、電話を受けることを意味します。
Jared Kells 2013

26

ただのメモ..

追加:

android:name=".Globals"

または、サブクラスに既存の <application>タグに名前を付けたもの。<application>マニフェストに別のタグを追加しようとすると、例外が発生します。


こんにちは、Gimbl。私も同じ問題を抱えていました。また、独自の<application>タグがあり、別の<application>タグを追加しようとすると、同じ問題が発生しました(例外メッセージ)。しかし、私はあなたが言ったことをしました、そしてそれはうまくいきませんでした。<application>タグにandroid:name = "。GlobalClass"を追加しましたが、機能しません。どのようにそれを解決したかを完全に説明できますか?
Sonhja、2011

3
良い <manifest> <application android:name = "。GlobalData"> </ application> </ manifest>。 悪い <manifest> <application> </ application> <application android:name = "
。GlobalData

13

アプリケーションタグの指定方法も見つかりませんでしたが、多くのGooglingの結果、マニフェストファイルのドキュメントから明らかになりました。アプリケーションスタンザのデフォルトのアイコンとラベルに加えて、android:nameを使用します。

android:nameアプリケーションに実装されたApplicationサブクラスの完全修飾名。アプリケーションプロセスが開始されると、このクラスはアプリケーションのコンポーネントの前にインスタンス化されます。

サブクラスはオプションです。ほとんどのアプリケーションでは必要ありません。サブクラスがない場合、Androidは基本のApplicationクラスのインスタンスを使用します。


13

そのようなグローバル構造を持つネイティブメモリのコレクションを確保することについてはどうでしょうか?

アクティビティにはonPause/onDestroy()破棄時に呼び出されるメソッドがありますが、Applicationクラスには同等のメソッドがありません。アプリケーションが強制終了されたとき、またはタスクスタックがバックグラウンドに置かれたときに、グローバル構造(特にネイティブメモリへの参照を含む構造)が適切にガベージコレクションされるようにするために推奨されるメカニズムは何ですか?


1
明白な解決策は、ネイティブリソースを担当するオブジェクトにCloseableインターフェースを実装し、try-with-resourcesステートメントなどでそれらが確実に管理されるようにすることです。最悪の場合、常にオブジェクトファイナライザを使用できます。
2014年

5

以下のように機能するアプリケーション名を定義する必要があるだけです。

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

4

上記で説明したように、OSは通知なしにアプリケーションを終了する可能性があるため(onDestroyイベントはありません)、これらのグローバル変数を保存する方法はありません。

SharedPreferencesは、COMPLEX STRUCTURED変数がある場合を除いて解決策になる可能性があります(私の場合、ユーザーが既に処理したIDを格納する整数配列がありました)。SharedPreferencesの問題は、値が必要になるたびにこれらの構造を保存および取得することが難しいことです。

私の場合、バックグラウンドサービスがあり、この変数をそこに移動できました。サービスにはonDestroyイベントがあるため、これらの値を簡単に保存できました。


onDestroy()は、サービスに対しても呼び出されることが保証されていません。
OpenGL ESを学ぶ2013

はい、これは発生する可能性がありますが、重大な状況の場合のみです。
Adorjan Princz 2013

4

sqliteに格納されている変数があり、アプリのほとんどのアクティビティでそれらを使用する必要がある場合。次に、アプリケーションはおそらくそれを達成するための最良の方法です。アプリケーションの起動時にデータベースから変数をクエリし、フィールドに格納します。次に、これらの変数をアクティビティで使用できます。

だから正しい方法を見つけてください、そして最善の方法はありません。


3

この種の状態を格納する静的フィールドを持つことができます。または、リソースバンドルに配置して、onCreate(Bundle savedInstanceState)からそこから復元します。Androidアプリが管理するライフサイクルを完全に理解していることを確認してください(例:キーボードの向きを変更するとlogin()が呼び出される理由)。


2

<application>マニフェストファイルで 別のタグを使用しないでください。既存の<application>tagを1つ変更するだけandroid:name=".ApplicationName"で、作成ApplicationNameするサブクラス(グローバルの格納に使用)の名前になるこの行を追加します。

したがって、最終的にマニフェストファイル内のONE AND ONLY <application>タグは次のようになります。

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

1

インテント、Sqlite、または共有設定を使用できます。ドキュメント、写真、ビデオなどのメディアストレージに関しては、代わりに新しいファイルを作成できます。


1

これを行うには、次の2つの方法があります。

  1. Applicationクラスの使用
  2. 共有設定の使用

  3. Applicationクラスの使用

例:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

上記のクラスを使用して、以下のようにMainActivityにログインを実装できます。コードは次のようになります。

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

この方法は一時的なストレージに有効です。メモリ不足のため、オペレーティングシステムがアプリケーションを強制終了するタイミングはわかりません。アプリケーションがバックグラウンドにあり、ユーザーが実行するためにより多くのメモリを必要とする他のアプリケーションをナビゲートしている場合、オペレーティングシステムはバックグラウンドよりもフォアグラウンドプロセスを優先するため、アプリケーションは強制終了されます。したがって、ユーザーがログアウトするまで、アプリケーションオブジェクトはnullになります。したがって、このために、上記の2番目の方法を使用することをお勧めします。

  1. 共有設定の使用。

    String MYPREF="com.your.application.session"
    
    SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
    
    //Insert key as below:
    
    Editot editor= pref.edit();
    
    editor.putString("key","value");
    
    editor.commit();
    
    //Get key as below.
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
    String key= getResources().getString("key");

0

アクティビティの結果は、再開の前に呼び出されます。したがって、ログインチェックを再開時に移動し、2番目のログインは、2番目のアクティビティが肯定的な結果を返したらブロックすることができます。on resumeは毎回呼び出されるので、最初に呼び出されない心配はありません。


0

サブクラス化のアプローチは、BARACUSフレームワークでも使用されています。私の観点から見ると、アプリケーションのサブクラス化は、Androidのライフサイクルで動作するように意図されていました。これは、任意のアプリケーションコンテナが行うことです。次に、グローバルを持つ代わりに、このコンテキストにBeanを登録し、コンテキストで管理可能なクラスに挿入します。注入されたすべてのBeanインスタンスは、実際にはシングルトンです。

詳細については、この例を参照してください

あなたがもっと多くを手に入れることができるのに、なぜ手動作業をするのですか?


0
class GlobaleVariableDemo extends Application {

    private String myGlobalState;

    public String getGlobalState(){
     return myGlobalState;
    }
    public void setGlobalState(String s){
     myGlobalState = s;
    }
}

class Demo extends Activity {

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

0

クラスを拡張するクラスを作成Applicationし、変数をそのクラスのフィールドとして宣言し、それにゲッターメソッドを提供することができます。

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

そして、アクティビティでその変数にアクセスするには、これを使用します:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.