Android – Le langage WebView change brusquement sur Android N

J’ai une application multilingue avec langue primaire anglais et langue secondaire arabe.

Comme décrit dans la documentation ,

  • J’ai ajouté android:supportsRtl="true" dans le manifeste.
  • J’ai modifié toutes les propriétés XML avec les atsortingbuts left et right pour start et end respectivement.
  • J’ai ajouté des chaînes en arabe dans ssortingngs-ar (et de la même manière pour d’autres ressources).

La configuration ci-dessus fonctionne correctement. Après avoir modifié les Locale en ar-AE , le texte et les ressources en arabe sont correctement affichés dans mes activités.

Cependant, chaque fois que je navigue vers une Activity avec WebView et / ou WebViewClient , les parameters régionaux, de texte et de disposition reviennent brusquement à la valeur par défaut du périphérique.

Autres indications:

  • Cela se produit uniquement sur un Nexus 6P avec Android 7.0 . Tout fonctionne correctement sur Android 6.0.1 et ci-dessous.
  • Le changement brusque de parameters régionaux se produit uniquement lorsque je navigue vers une Activity qui possède une WebView et / ou un WebViewClient (et j’en ai plusieurs). Cela ne se produit sur aucune des autres activités.

Android 7.0 prend en charge plusieurs parameters régionaux, ce qui permet à l’utilisateur de définir plusieurs parameters régionaux par défaut. Donc, si je mets la locale principale à Locale.UK :

entrer la description de l'image ici

Ensuite, lorsque vous naviguez vers WebView , les parameters régionaux passent de ar-AE à en-GB .

Modifications de l’API Android 7.0:

Comme indiqué dans la liste des modifications de l’API , de nouvelles méthodes relatives aux parameters régionaux ont été ajoutées aux classes suivantes dans l’API 24:

Locale :

  • Locale.getDefault(...)
  • Locale.setDefault(...)

Configuration :

  • getLocales()
  • setLocales(...)

Cependant, je construis mon application avec l’API 23 et n’utilise aucune de ces nouvelles méthodes.

En outre …

  • Le problème se produit également sur l’émulateur Nexus 6P.

  • Pour obtenir les parameters régionaux par défaut, j’utilise Locale.getDefault() .

  • Pour définir les parameters régionaux par défaut, j’utilise le code suivant:

     public static void setLocale(Locale locale){ Locale.setDefault(locale); Configuration config = new Configuration(); config.setLocale(locale); Context context = MyApplication.getInstance(); context.getResources().updateConfiguration(config, context.getResources().getDisplayMesortingcs()); } 

Quelqu’un at-il déjà rencontré ce problème? Quelle est la raison de cela et comment puis-je résoudre ce problème?

Les références:

1. Prise en charge native de RTL sous Android 4.2 .

2. Support multilingue – Langue et parameters régionaux .

3. Méfiez-vous des parameters régionaux par défaut .

La réponse de Ted Hopp a réussi à résoudre le problème, mais il n’a pas abordé la question de savoir pourquoi cela se produit.

La raison en est les modifications apscopes à la classe WebView et à son package de support dans Android 7.0.

Contexte:

WebView d’Android est construit en utilisant WebKit . Alors qu’il s’agissait à l’origine d’une partie de AOSP, KitKat a décidé de séparer WebView en un composant distinct appelé Android System WebView . Il s’agit essentiellement d’une application système Android pré-installée avec les appareils Android. Il est périodiquement mis à jour, tout comme les autres applications système telles que les services Google Play et l’application Play Store. Vous pouvez le voir dans votre liste d’applications système installées:

Système Android WebView

Android 7.0 change :

À partir d’Android N, l’application Chrome sera utilisée pour le rendu de toutes les WebView dans des applications Android tierces. Dans les téléphones équipés de Android N prêts à l’emploi, l’application Android WebView System n’est pas présente du tout. Dans les appareils ayant reçu une mise à jour OTA vers Android N, le système Android WebView est désactivé:

WebView désactivé

et

WebView désactivé

De plus, la prise en charge de plusieurs parameters régionaux a été introduite, les périphériques ayant plusieurs langues par défaut:

entrer la description de l'image ici

Cela a une conséquence importante pour les applications qui ont plusieurs langues. Si votre application a WebView , celles-ci sont rendues à l’aide de l’application Chrome. Étant donné que Chrome est une application Android en ellemême , exécutée dans son propre processus en mode bac à sable, elle ne sera pas liée aux parameters régionaux définis par votre application. Au lieu de cela, Chrome reviendra aux parameters régionaux du périphérique principal. Par exemple, disons que les parameters régionaux de votre application sont définis sur ar-AE , alors que les parameters régionaux principaux du périphérique sont en-US . Dans ce cas, les parameters régionaux de l’ Activity contenant une vue WebView passeront de ar-AE à en-US , et les chaînes et les ressources des dossiers de parameters régionaux correspondants seront affichées. Vous pouvez voir un mélange de chaînes / ressources LTR et RTL sur les Activity qui ont WebView .

La solution:

La solution complète à ce problème comprend deux étapes:

ÉTAPE 1:

Tout d’abord, réinitialisez les parameters régionaux par défaut manuellement dans chaque Activity , ou au moins chaque Activity disposant d’une vue WebView .

 public static void setLocale(Locale locale){ Locale.setDefault(locale); Context context = MusafirApplication.getInstance(); final Resources resources = context.getResources(); final Configuration config = resources.getConfiguration(); config.setLocale(locale); context.getResources().updateConfiguration(config, resources.getDisplayMesortingcs()); } 

Appelez la méthode ci-dessus avant d’appeler setContentView(...) dans la méthode onCreate() de toutes vos activités. Le locale régional doit être le paramètre Locale par défaut que vous souhaitez définir. Par exemple, si vous souhaitez définir l’arabe / UAE comme paramètre régional par défaut, vous devez passer la new Locale("ar", "AE") . Ou si vous souhaitez définir les parameters régionaux par défaut (c.-à-d. Les parameters Locale définis automatiquement par le système d’exploitation), vous devez transmettre Locale.US .

ÉTAPE 2:

En outre, vous devez append la ligne de code suivante:

 new WebView(this).destroy(); 

dans onCreate() de votre classe Application (si vous en avez une), et où que ce soit, l’utilisateur peut changer la langue. Cela prendra en charge toutes sortes de cas extrêmes qui peuvent survenir au redémarrage de l’application après avoir changé la langue (vous avez peut-être remarqué des chaînes dans d’autres langues ou avec l’alignement opposé après avoir modifié la langue des Activities ayant WebView sous Android 7.0 ++) .

En complément, les tabs personnalisés de Chrome sont désormais le moyen privilégié de rendre les pages Web intégrées aux applications.

Les références:

1. Android 7.0 – modifications pour WebView .

2. Comprendre les correctifs de sécurité WebView et Android .

3. WebView pour Android .

4. WebView: de “Powered by Chrome” à Google Chrome .

5. Nougat WebView .

6. Android 7.0 Nougat .

7. Android N Mysteries, Partie 1: Système Android WebView est juste “Chrome” maintenant? .

Votre code semble définir les parameters régionaux dans la configuration de l’application elle-même ( MyApplication.getInstance() ). Cependant, vous devez mettre à jour la configuration du contexte d’activité avant de gonfler l’affichage du contenu de l’activité. J’ai trouvé que la modification du contexte de l’application ne suffisait pas (et, en fait, n’est même pas nécessaire). Si je ne mets pas à jour chaque contexte d’activité, le comportement est incohérent entre les activités.

La façon dont je l’aborde consiste à sous- AppCompatActivity (ou Activity , si ce n’est pas la bibliothèque de compatibilité), puis à dériver toutes mes classes d’activité de cette sous-classe. Voici une version simplifiée de mon code:

 public class LocaleSensitiveActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { Locale locale = ... // the locale to use for this activity fixupLocale(this, locale); super.onCreate(savedInstanceState); ... } static void fixupLocale(Context ctx, Locale newLocale) { final Resources res = ctx.getResources(); final Configuration config = res.getConfiguration(); final Locale curLocale = getLocale(config); if (!curLocale.equals(newLocale)) { Locale.setDefault(newLocale); final Configuration conf = new Configuration(config); conf.setLocale(newLocale); res.updateConfiguration(conf, res.getDisplayMesortingcs()); } } private static Locale getLocale(Configuration config) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return config.getLocales().get(0); } else { //noinspection deprecation return config.locale; } } } 

Ensuite, je m’assure d’appeler super.onCreate(savedInstanceState) dans la méthode super.onCreate(savedInstanceState) de chaque sous-classe avant d’ appeler des méthodes (telles que setContentView() ) qui utilisent le contexte.

Après avoir lu toutes les réponses, j’ai découvert qu’il manquait quelque chose à chacun, alors voici la solution qui a fonctionné pour moi jusqu’à présent. WebView ayant la priorité sur la configuration linguistique du contexte de l’application et du contexte de l’application, vous devez vous assurer qu’à chaque fois que cela se produit, vous appelez une méthode qui réinitialise ces modifications. Dans mon cas, j’ai écrit le cours suivant que mes activités qui présentent ce problème s’étendent (celles montrant une WebView):

 public class WebViewFixAppCompatActivity extends AppCompatActivity { private Locale mBackedUpLocale = null; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { mBackedUpLocale = getApplicationContext().getResources().getConfiguration().getLocales().get(0); } } @Override protected void onStop() { super.onStop(); fixLocale(); } @Override public void onBackPressed() { fixLocale(); super.onBackPressed(); } /** * The locale configuration of the activity context and the global application context gets overridden with the first language the app supports. */ public void fixLocale() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Resources resources = getResources(); final Configuration config = resources.getConfiguration(); if (null != mBackedUpLocale && !config.getLocales().get(0).equals(mBackedUpLocale)) { Locale.setDefault(mBackedUpLocale); final Configuration newConfig = new Configuration(config); newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry())); resources.updateConfiguration(newConfig, null); } // Also this must be overridden, otherwise for example when opening a dialog the title could have one language and the content other, because // different contexts are used to get the resources. Resources appResources = getApplicationContext().getResources(); final Configuration appConfig = appResources.getConfiguration(); if (null != mBackedUpLocale && !appConfig.getLocales().get(0).equals(mBackedUpLocale)) { Locale.setDefault(mBackedUpLocale); final Configuration newConfig = new Configuration(appConfig); newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry())); appResources.updateConfiguration(newConfig, null); } } } } 

L’idée affichée par @Tobliug pour sauvegarder la configuration initiale avant que WebView ne l’emporte sur elle a fonctionné pour moi, dans mon cas particulier, j’ai trouvé que c’était plus facile à implémenter que les autres solutions publiées. Il est important que la méthode de réparation soit appelée après avoir quitté WebView, par exemple lors de la pression sur et sur onStop. Si webView est affiché dans une boîte de dialog, vous devez vous assurer que la méthode du correctif est appelée après avoir rejeté la boîte de dialog, principalement dans onResume et / ou onCreate. Et si webView est directement chargé dans onCreate de l’activité et non après dans un nouveau fragment, le correctif doit également être appelé directement après setContentView avant que le titre de l’activité ne soit défini, etc. Si WebView est chargé dans un fragment de l’activité, appelez L’activité dans onViewCreated du fragment et de l’activité doit appeler la méthode fix. Toutes les activités n’ont pas besoin d’étendre la classe ci-dessus, comme indiqué dans une réponse, ce qui est excessif et inutile. Ce problème ne peut pas non plus être résolu en remplaçant les tabs WebView par Google Chrome ou en ouvrant un navigateur externe.

Si vous avez vraiment besoin de configurer vos ressources pour avoir la liste complète des langues et pas seulement une, vous devrez alors fusionner cette solution avec celle de https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce pas nécessaire.

Je n’ai pas non plus trouvé nécessaire d’appeler le nouveau WebView (this) .destroy (); comme noté dans une réponse ici.

Même problème ici. J’ai une solution sale mais simple.

Comme j’observe que les parameters régionaux sont toujours bons dans la fonction Activity.onCreate (…) et qu’ils ne sont plus valides dans la fonction Activity.onPostCreate (…), je sauvegarde simplement les parameters régionaux et les force à la fin de onPostCreate (…) fonction.

Et c’est parti :

 private Locale backedUpLocale = null; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); backedUpLocale = getApplicationContext().getResources().getConfiguration().locale; } @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); changeLocale(backedUpLocale); } 

Bonus – la fonction locale de changement:

 public void changeLocale(final Locale locale) { final Configuration config = res.getConfiguration(); if(null != locale && !config.locale.equals(locale)) { Locale.setDefault(locale); final Configuration newConfig = new Configuration(config); if(PlatformVersion.isAtLeastJellyBeanMR1()) { newConfig.setLocale(new Locale(locale.getLanguage())); } else { newConfig.locale = new Locale(locale.getLanguage()); } res.updateConfiguration(newConfig, null); } } 

Espère que ça va aider.

Aucune des réponses ci-dessus ne m’a aidé, j’ai réussi à réinitialiser les parameters régionaux de l’application à nouveau dans la méthode onStop () de l’activité contenant la vue Web

Si vous utilisez WebView uniquement pour afficher du texte enrichi (du texte avec des paragraphes ou du texte en gras et en italique dans différentes tailles de police), vous pouvez utiliser TextView et Html.fromHtml () . Les TextViews n’ont aucun problème avec les parameters régionaux 😉

Je veux append un autre cas d’utilisation ici:

Lorsque vous appuyez à nouveau sur l’activité de la vue Web (affichage de l’écran de paiement et du bouton de l’utilisateur), onCreate () de l’activité précédente ne s’exécute pas. Cette langue a donc été réinitialisée. Pour éviter tout bug, nous devons réinitialiser les parameters régionaux de l’application dans onResume() de l’activité de base.

 private static void updateResources(Context context, Ssortingng language) { Locale locale = new Locale(language); Locale.setDefault(locale); Configuration config = new Configuration(); config.setLocale(locale); config.setLayoutDirection(locale); context.getResources().updateConfiguration(config, context.getResources().getDisplayMesortingcs()); } 

Appelez la méthode ci-dessus dans onResume () de l’activité de base ou au moins dans l’activité de la vue Web.

Modifier: si vous traitez des fragments, assurez-vous que cette méthode est appelée lorsque l’utilisateur quitte Webview.

Dans Android N, lorsque vous new WebView() , il ajoute /system/app/WebViewGoogle/WebViewGoogle.apk au chemin de la ressource et, s’il n’a pas été ajouté au chemin, Resource recreate.

Donc, si vous voulez résoudre la question, faites simplement une new WebView(application) avant de changer le local.

Si vous connaissez le chinois, vous pouvez lire ce blog .