web-dev-qa-db-ja.com

onCreate()の前に静的文字列でgetString()を使用する方法は?

アクティビティが作成される前に、getString()を使用してリソースから文字列を取得し、文字列配列に割り当てようとしています。

_private static final String[] MenuNames = {
    Resources.getSystem().getString(R.string.LCMeterMenu),
    Resources.getSystem().getString(R.string.FrecMenu),
    Resources.getSystem().getString(R.string.LogicAnalyzerMenu),
    "Prueba con achartengine",
    Resources.getSystem().getString(R.string.BrazoMenu)
};
_

Resources.getSystem().getString(R.string.LCMeterMenu)を使用すると、Eclipseは文句を言いませんが、実行時にエラーが発生します。

原因:Android.content.res.Resources $ NotFoundException:文字列リソースID#0x7f0a000a

しかし、onCreate()の中に入れると:

_Log.i("StringR", "String: " + getString(R.string.LCMeterMenu));
_

文字列を取得しましたが、前に定義した最後の文字列に割り当てることができません。 getString()の前にonCreate()のみを使用すると、静的エラーメッセージが表示されます。グローバル変数のonCreate()の前にリソースを使用するにはどうすればよいですか?

18
Andres

リソースから_static final_フィールドを初期化することはできません。フィールドは、クラスが初期化されるときに初期化される必要があります。これは、実行時にアプリケーションリソースがバインドされる前に行われます。 (ちなみに、Resources.getSystem()を使用できない理由は、その方法で取得したResourcesオブジェクトには、アプリケーションリソースではなく、システムリソースのみが含まれているためです。)

アプリケーションリソースがバインドされる前にこれらの文字列を使用可能にする必要がある場合、実行する唯一の実用的なことは、文字列をコードに直接配置することです。ただし、「Androidの方法」はコードを整理することであるため、初期化はonCreate()の最中(またはその後)にのみ行う必要があります。 onCreate()で文字列配列を初期化するだけで、フィールドを静的または最終にする必要はありません。

文字列配列を特定のアクティビティに関連付けたくない場合は、Applicationをサブクラス化し、アプリケーションクラスのonCreate()メソッド内のリソースから配列を読み取ることができます。 (マニフェストでカスタムアプリケーションクラスを宣言する必要もあります。)ただし、 docs このようなアプローチはお勧めしません。 (配列はプライベートであるため、とにかく単一のアクティビティに密接に関連していると思われます。したがって、Applicationサブクラスの使用は保証されていないようです。)

別の方法は、配列のシングルトンクラスを宣言することです。シングルトンアクセサ関数には、必要に応じてリソースを取得できるようにContextが必要です。

_public class StringArray {
    private static String[] theArray;
    public static String[] getArray(Context context) {
        if (theArray == null) {
            theArray = context.getResources().getStringArray(R.array.my_strings);
        }
        return theArray;
    }
}
_

(これは、文字列データが、彼の回答で提案された@JaiSoniのような_<string-array>_リソースで定義されていることを前提としています。)ここでも、メンバーフィールドをfinalとして宣言することはできません。

27
Ted Hopp

いいえ、onCreate()の前にリソースを使用することはできません。 onCreate()を使用すると、getResources()でResourcesのインスタンスを取得できます。ここですべての文字列を取得できます。また、文字列はstrings.xmlで定義することにより、すでに静的として宣言されています。

リソースにアクセスするための擬似コード、

Resources res = getResources();
String app_name = res.getString(R.string.app_name);
4
Lalit Poptani

別のアプローチは、静的配列をリソース識別子(リソース自体ではなくすでに利用可能)で初期化することです。

private static final int[] MenuNames = {
    R.string.LCMeterMenu,
    R.string.FrecMenu,
    ...
};

このようにして、リソースのロードを実際に利用可能になるまで延期できます。

String s = getResources().getString(MenuNames[i]);
3
bompf

以下は、_static final_などのXMLからAndroidの_strings.xml_変数を初期化するための実用的なアプローチです。

  1. アプリケーションをサブクラス化し、「静的コンテキスト」を提供します
  2. マニフェストにアプリケーションクラスを登録する
  3. 静的コンテキストを使用して定数を初期化します

1。_MyApplication.Java_

_public abstract class MyApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    /**
     * Returns a "static" application context. Don't try to create dialogs on
     * this, it's not gonna work!
     * 
     * @return
     */
    public static Context getContext() {
        return context;
    }
}
_

2。_AndroidManifest.xml_

_<application
    Android:name=".Android.application.MyApplication"
    <!-- ... -->
</application>
_

3。アプリケーションコード(例:アクティビティ

_private static final String[] MenuNames = {
    getContext().getString(R.string.LCMeterMenu),
    getContext().getString(R.string.FrecMenu),
    getContext().getString(R.string.LogicAnalyzerMenu),
    "Prueba con achartengine",
    getContext().getString(R.string.BrazoMenu)
};
protected static Context getContext() {
    return MyApplication.getContext();
}
_

実例については、 AbstractApplication および PreferencesServiceSharedPreferences を参照してください。

このアプローチには欠点もあることに注意してください。

  • 「Androidの方法」に反対することは別として(@Ted Hoppが 彼の答え で提案したように)、
  • テストが少し難しくなります。そのため、MyApplication.getContext()の呼び出しは別のメソッドでラップされます。これは静的メソッドであるため、テストコードでオーバーライドするのは簡単ではありません。ただし、この目的には Powermock などのフレームワークを使用できます。
  • さらに、それはNullPointerExceptionsに少し傾向があります。コンテキストがnullになるとすぐに(たとえば、テストコード内で)、アプリケーションコードがクラッシュします。これを克服する1つのオプションは、コンストラクターで初期化を行うことです。コンストラクターでは、getContext() return nullに反応できます( を参照)。
1
schnatterer

getString(int resId)によって得られるものはすべて、アプリケーションにとってすでに定数になっています。なぜそれを別のfinal static変数に保持する必要があるのですか。いつでも好きなときに読むことができますよね?

0
midhunhk