Подтвердить что ты не робот

Как изменить название мероприятия в приложении attach()

Я хочу запустить параметризованный Testmentation Test с различными локалями для запуска того же теста со всеми поддерживаемыми языками.

Наблюдаемое поведение заключается в том, что активность будет иметь локализованный заголовок первого тестового прогона также для каждого следующего запуска. Поэтому, независимо от того, на каком языке находится мой телефон, заголовок будет правильно локализован для первого параметризованного тестового прогона и по-прежнему будет одинаковым для каждого следующего.

В то время как перезапись локалей сама работает для любых ресурсов, она будет работать только один раз для названия действия, если она установлена ​​ AndroidManifest.xml.

Кажется, что действия заставляют их заголовок устанавливаться один раз в attach, и все, что называется приложением attach, похоже, кэширует заголовок в локали, приложение было впервые запущено.

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
   ---> CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
    attachBaseContext(context);

Поскольку ресурсы всегда корректно локализованы, обходным решением было бы вызвать setTitle(R.string.title) или просто getActionBar().setTitle(R.string.setTitle), но я бы не хотел изменять действия исключительно для целей тестирования.

Вопрос: Как я могу изменить заголовок, который запускается в действие после первого тестового прогона? Как уже упоминалось выше, это похоже на кэширование и не обновляется должным образом, а убийство приложения для его перезапуска приведет к сбою в контрольном тестировании.

Настройка тестирования

Весь тестовый проект может быть найденный здесь, на GitHub (Localization.java содержит текущие неудачные модульные тесты с проблемой, описанной здесь) и используя параметр Unit Test в сочетании с UIAutomator.

Цель состоит в том, чтобы взять партию скриншотов, не зная слишком много о самом приложении (UIAutomator), и приложение не должно быть изменено для теста.

Изменение языкового стандарта:

Я успешно меняю языковой стандарт перед каждым тестом, и мои тексты корректно отображаются, выполняя следующие действия, также я имею несколько утверждений, убедившись, что ресурсы на самом деле являются правильными языками.

public LocalizationTest(Locale locale) {
    mLocale = locale;
    Configuration config = new Configuration();
    Locale.setDefault(mLocale);
    config.setLocale(mLocale);

    Resources resources = InstrumentationRegistry.getTargetContext().getResources();
    resources.updateConfiguration(config, resources.getDisplayMetrics());

    resources.flushLayoutCache();
}

Что не работает:

Я, очевидно, попытался установить локаль таким же образом в целевом контексте, контексте приложения и активности (что, вероятно, было бы слишком поздно).

Я вижу, что attach вызывается из Instrumentation, но просто создание нового приложения и попытка его запуска не будут также локализовать заголовок.

Intent intent = context.getPackageManager().getLaunchIntentForPackage(BuildConfig.APPLICATION_ID);
context = InstrumentationRegistry.getInstrumentation().newApplication(App.class,
                InstrumentationRegistry.getTargetContext());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
4b9b3361

Ответ 1

Строка заголовка кэшируется в диспетчере пакетов ApplicationPackageManager в статическом sStringCache.

Пока существует метод static void configurationChanged(), который очищает кеш, он, похоже, не активируется при ручном изменении. Следовательно, описываемая проблема с неправильно локализованным названием активности после первого вызова.

Решение этого стало возможным с помощью отражения для загрузки класса и вызова метода. Это грязно, поскольку он обращается к частному методу, но он работает.

// as before
Configuration config = new Configuration();
Locale.setDefault(mLocale);
config.setLocale(mLocale);

Resources resources = context.getResources();
resources.updateConfiguration(config, resources.getDisplayMetrics());

// CLEAR the cache!
Method method = getClass().getClassLoader()
        .loadClass("android.app.ApplicationPackageManager")
        .getDeclaredMethod("configurationChanged");
method.setAccessible(true);
method.invoke(null);

В качестве альтернативы вы можете использовать общедоступные методы для другого непубличного API, который, в свою очередь, также вызовет вышеупомянутый метод. Все еще грязные, но не вызывающие частные методы.

Кажется, вы можете опустить resources.updateConfiguration(...); с помощью этого метода, хотя он также позаботится об этом.

// Clear the cache. 
Object thread = getClass().getClassLoader()
        .loadClass("android.app.ActivityThread")
        .getMethod("currentActivityThread")
        .invoke(null);
Method method = getClass().getClassLoader()
        .loadClass("android.app.ActivityThread")
        .getMethod("applyConfigurationToResources", Configuration.class);
method.invoke(thread, config);

Ответ 2

Мы выяснили, что заголовок активности установлен в onAttach() с заголовком, предоставленным менеджером активности. Следовательно, я думаю, вам нужно изменить локаль системы.

Для этого тест может использовать отражение на ActivityManagerNative для обновления конфигурации:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public Localization(Locale locale) throws ClassNotFoundException, NoSuchMethodException,
        InvocationTargetException, IllegalAccessException {
    Context context = InstrumentationRegistry.getTargetContext();
    log(context.toString());
    log(context.getApplicationContext().toString());

    mLocale = locale;

    Class<?> amClass = Class.forName("android.app.ActivityManagerNative");
    Method getDefaultMethod = amClass.getDeclaredMethod("getDefault");
    Object iActivityManager = getDefaultMethod.invoke(null /* static method */);
    Method updateConfigurationMethod =
            amClass.getMethod("updateConfiguration", Configuration.class);
    Configuration configuration = new Configuration(context
            .getResources().getConfiguration());
    configuration.locale = locale;
    updateConfigurationMethod.invoke(iActivityManager, configuration);
}

Для этого дайте разрешение своему приложению (это разрешение подписано с помощью отладочных ключей, этого недостаточно для добавления в AndroidManifest)

adb shell pm grant at.bleeding182.testing.instrumentationtest android.permission.CHANGE_CONFIGURATION

Я протестировал это решение и могу подтвердить, что локаль теперь правильно изменена, а тест проходит - makeScreenshot довольно шелушащийся, но это еще один день.

Вам нужно будет изменить рабочий процесс вашего программного обеспечения factory:

  • Обновите приложение apk и apk apk
  • Предоставьте android.permission.CHANGE_CONFIGURATION для теста apk
  • Запустите тесты