ОБНОВЛЕНИЕ 13.02.2012: Приняли ответ, объяснили, что это поведение является ошибкой, и отметили, что оно исчезло в эмуляторах лучше, чем v 1.6, что делает его без проблем для большинства из нас. Обходной путь заключается в том, чтобы просто зациклить / уснуть, пока getContext (). GetApplicationContext () вернет ненулевое значение.
КОНЕЦ ОБНОВЛЕНИЯ
В соответствии с javadoc android.app.Application, я определил синглтон (называемый базой данных), к которому все мои действия обращаются к состоянию и постоянным данным, а Database.getDatabase (Context) получает контекст приложения через Context.getApplicationContext (). Эта настройка работает так, как объявлено, когда действия передаются в getDatabase (Context), но когда я запускаю модульный тест из AndroidTestCase, вызов getApplicationContext () часто возвращает ноль, хотя чем длиннее тест, тем чаще он возвращает ненулевой значение.
Следующий код воспроизводит нулевое значение в AndroidTestCase - синглтон не нужен для демонстрации.
Во-первых, чтобы регистрировать сообщения о создании приложений, в тестируемом приложении я определил MyApp и добавил его в манифест.
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Log.i("MYAPP", "this=" + this);
Log.i("MYAPP", "getAppCtx()=" + getApplicationContext());
}
}
Затем я определил тестовый пример для отчета по AndroidTestCase.getContext () 4 раза, разделенных несколькими снами и вызовом getSharedPreferences ():
public class DatabaseTest extends AndroidTestCase {
public void test_exploreContext() {
exploreContexts("XPLORE1");
getContext().getSharedPreferences("foo", Context.MODE_PRIVATE);
exploreContexts("XPLORE2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exploreContexts("XPLORE3");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exploreContexts("XPLORE4");
}
public void exploreContexts(String tag) {
Context testContext = getContext();
Log.i(tag, "testCtx=" + testContext +
" pkg=" + testContext.getApplicationInfo().packageName);
Log.i(tag, "testContext.getAppCtx()=" + testContext.getApplicationContext());
try {
Context appContext = testContext.createPackageContext("com.foo.android", 0);
ApplicationInfo appInfo = appContext.getApplicationInfo();
Log.i(tag, "appContext=" + appContext +
" pkg=" + appContext.getApplicationInfo().packageName);
Log.i(tag, "appContext.getAppCtx()=" + appContext.getApplicationContext());
} catch (NameNotFoundException e) {
Log.i(tag, "Can't get app context.");
}
}
}
И это фрагмент получившегося logCat (эмулятор 1.6 на SDK11 WinXP через Eclipse):
INFO/TestRunner(465): started: test_exploreContext(test.foo.android.DatabaseTest)
INFO/XPLORE1(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE1(465): testContext.getAppCtx()=null
INFO/XPLORE1(465): appContext=android.app.ApplicationContext@437801e8 pkg=com.foo.android
INFO/XPLORE1(465): appContext.getAppCtx()=null
INFO/XPLORE2(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE2(465): testContext.getAppCtx()=null
INFO/XPLORE2(465): appContext=android.app.ApplicationContext@43782820 pkg=com.foo.android
INFO/XPLORE2(465): appContext.getAppCtx()=null
INFO/MYAPP(465): this=com.foo.android.MyApplication@43783830
INFO/MYAPP(465): getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE3(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE3(465): testContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE3(465): appContext=android.app.ApplicationContext@43784768 pkg=com.foo.android
INFO/XPLORE3(465): appContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE4(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE4(465): testContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE4(465): appContext=android.app.ApplicationContext@43785778 pkg=com.foo.android
INFO/XPLORE4(465): appContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/TestRunner(465): finished: test_exploreContext(test.foo.android.DatabaseTest)
Обратите внимание, что getApplicationContext () некоторое время возвращал значение null, а затем начал возвращать экземпляр MyApp. Я не смог получить одинаковые результаты при разных запусках этого теста (вот как я закончил на 4 итерациях, в спящем режиме и с помощью вызова getSharedPreferences (), чтобы попытаться вывести приложение из строя).
Часть сообщений LogCat, представленных выше, казалась наиболее актуальной, но весь LogCat для этого отдельного прогона этого одиночного теста был интересен. Android запустил 4 AndroidRuntimes; кусок выше был с 4-го. Интересно, что 3-я среда выполнения отображала сообщения, указывающие, что она создала другой экземпляр MyApp в идентификаторе процесса 447:
INFO/TestRunner(447): started: test_exploreContext(test.foo.android.DatabaseTest)
INFO/MYAPP(447): this=com.foo.android.MyApplication@437809b0
INFO/MYAPP(447): getAppCtx()=com.foo.android.MyApplication@437809b0
INFO/TestRunner(447): finished: test_exploreContext(test.foo.android.DatabaseTest)
Я предполагаю, что сообщения TestRunner (447) поступают от родительского тестового потока, который сообщает о своих дочерних процессах 465. Тем не менее, возникает вопрос: почему Android запускает AndroidTestCase до того, как его контекст должным образом подключен к экземпляру приложения
Обходной путь : Один из моих тестов, по-видимому, большую часть времени избегал нулей, если я сначала позвонил getContext().getSharedPreferences("anyname", Context.MODE_PRIVATE).edit().clear().commit();
, поэтому я продолжаю.
КСТАТИ : Если ответ «это ошибка Android, почему бы вам не отправить ее; черт, почему бы вам не исправить это?» тогда я был бы готов сделать оба. Я еще не сделал шаг к тому, чтобы стать баг-файлером или участником - возможно, сейчас хорошее время.