繁体   English   中英

如何在attach()中更改活动标题

[英]How to change activities title in attach()

我想运行一个参数化的Instrumentation 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包含当前失败的单元测试以及此处描述的问题),并且正在使用参数化单元测试和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();
}

什么行不通:

我显然尝试在目标上下文,应用程序上下文和活动上以相同的方式设置语言环境(反正可能为时已晚)。

我看到attachInstrumentation调用,但只是创建一个新的App并尝试启动活动也不会本地化标题。

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);

标题字符串在静态sStringCache的包管理器ApplicationPackageManager中缓存。

虽然有一个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);

我们发现活动标题在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是非常makeScreenshot ,但那是另一天。

您需要更改软件工厂的工作流程:

  1. 更新应用apk并测试apk
  2. 将android.permission.CHANGE_CONFIGURATION提供给测试apk
  3. 运行测试

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM