ОБНОВЛЕНИЕ 13 февраля 2012 г .: Принят ответ, объяснено, что такое поведение является ошибкой, и отмечено, что оно исчезло в эмуляторах лучше, чем v 1.6, что делает его не проблемой для большинства из нас. Обходной путь - просто зацикливаться/спать, пока getContext().getApplicationContext() не вернет ненулевое значение. ЗАВЕРШИТЬ ОБНОВЛЕНИЕ
В соответствии с android.app.Application javadoc я определил синглтон (называемый базой данных), к которому все мои действия обращаются для состояния и постоянных данных, а Database.getDatabase(Context) получает контекст приложения через Context.getApplicationContext(). Эта настройка работает, как рекламируется, когда действия передаются в getDatabase(Context), но когда я запускаю модульный тест из AndroidTestCase, вызов getApplicationContext() часто возвращает значение 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 среды выполнения Android; кусок выше был из 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();
, поэтому я использую его.
Кстати: если ответ "это ошибка Android, почему бы вам не зарегистрировать ее; черт возьми, почему бы вам ее не исправить?" тогда я был бы готов сделать и то, и другое. Я еще не решился стать баг-файлером или участником — может быть, сейчас самое подходящее время.
Nullpointer
исключений вonDestroy
, тогда как все поля экземпляра были правильно инициализированы вonCreate
. Вы можете проверить это, поставив точку останова вonDestroy
и увидеть, что выполнение прерывается там как минимум 4 раза, в то время как тест запускался только один раз. - person siamii   schedule 19.07.2011