Почему AndroidTestCase.getContext (). GetApplicationContext () возвращает значение null?

ОБНОВЛЕНИЕ 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,почему бы вам не подать его; черт возьми, почему бы тебе не исправить это? "Тогда я был бы готов сделать и то, и другое. Я еще не сделал шага, чтобы быть сообщником ошибок или участником - может быть, сейчас хорошее время.

21
задан Brian Tompsett - 汤莱恩 13 June 2015 в 12:53
поделиться