ОБНОВЛЕНИЕ 2/13/2012: Принял ответ, объяснил, что такое поведение является ошибкой, и отметил, что, похоже, он исчез в эмуляторах лучше, чем версия 1.6, что делает его не проблемой для большинства нас. Обходной путь - просто зацикливаться / засыпать, пока getContext (). GetApplicationContext () не вернет значение, отличное от null. КОНЕЦ ОБНОВЛЕНИЯ
В соответствии с javadoc android.app.Application, я определил синглтон (называемый базой данных), к которому все мои действия имеют доступ для состояния и постоянных данных, а Database.getDatabase (Context) получает контекст приложения через Context.getApplicationContext ( ). Эта настройка работает так, как рекламируется, когда действия передаются в getDatabase (Context), но когда я запускаю модульный тест из AndroidTestCase, вызов getApplicationContext () часто возвращает null, хотя чем дольше тест, тем чаще он возвращает ненулевое значение. значение.
Следующий код воспроизводит null в 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-го. Интересно, что третья среда выполнения отображала сообщения, указывающие на то, что она создала другой экземпляр 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 ();
сначала, так что я иду с этим.
BTW : Если ответ будет "это ошибка Android,почему бы вам не подать его; черт возьми, почему бы тебе не исправить это? "Тогда я был бы готов сделать и то, и другое. Я еще не сделал шага, чтобы быть сообщником ошибок или участником - может быть, сейчас хорошее время.