Skip to content

Android - Il linguaggio WebView cambia bruscamente su Android 7.0 e versioni successive

Dopo la nostra vasta raccolta di informazioni siamo stati in grado di risolvere questo dilemma che alcuni dei nostri lettori potrebbero avere. Ti diamo la soluzione e vogliamo servirti come un grande supporto.

Soluzione:

La risposta di Ted Hopp è riuscita a risolvere il problema, ma non ha affrontato la questione di perché questo si verifica.

Il motivo è da ricercare nelle modifiche apportate al file WebView e al suo pacchetto di supporto in Android 7.0.

Sfondo:

La classe WebView è costruito utilizzando WebKit. Mentre originariamente era una parte dell'AOSP, da KitKat in poi è stata presa la decisione di scorporare WebView in un componente separato chiamato Android System WebView. Si tratta essenzialmente di un'applicazione di sistema Android preinstallata nei dispositivi Android. Viene aggiornata periodicamente, proprio come altre app di sistema come Google Play Services e l'app Play Store. È possibile vederla nell'elenco delle app di sistema installate:

Sistema Android WebView

Modifiche ad Android 7.0:

A partire da Android N, l'app Chrome verrà utilizzata per eseguire il rendering di qualsiasi WebViewnelle applicazioni Android di terze parti. Nei telefoni che hanno Android N out-of-the-box, l'app Android WebView System non è affatto presente. Nei dispositivi che hanno ricevuto un aggiornamento OTA ad Android N, Android System WebView è disattivato:

WebView disabilitato

e

WebView disabilitato

Inoltre, è stato introdotto il supporto multi-locale, con dispositivi che hanno più di una lingua predefinita:

Immettere la descrizione dell'immagine qui

Questo ha una conseguenza importante per le applicazioni che hanno più lingue. Se l'applicazione ha WebViewvengono resi utilizzando l'applicazione Chrome. Poiché Chrome è un'applicazione Android di per sé, in esecuzione nel proprio processo sandbox, non sarà vincolato al locale impostato dall'applicazione. Al contrario, Chrome tornerà al locale primario del dispositivo. Ad esempio, se il locale dell'applicazione è impostato su ar-AEmentre il locale primario del dispositivo è en-US. In questo caso, il locale del dispositivo Activity contenente un WebView cambierà da ar-AE a en-USe verranno visualizzate le stringhe e le risorse delle cartelle locali corrispondenti. Si potrebbe vedere un miscuglio di stringhe/risorse LTR e RTL in quelle cartelle Activityche hanno WebViews.

La soluzione:

La soluzione completa di questo problema consiste in due passaggi:

FASE 1:

Per prima cosa, reimpostare manualmente il locale predefinito in ogni Activityo almeno in ogni Activity che ha un nome WebView.

public static void setLocale(Locale locale){
    Context context = MyApplication.getInstance();
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    Locale.setDefault(locale);
    configuration.setLocale(locale);

    if (Build.VERSION.SDK_INT >= 25) {
        context = context.getApplicationContext().createConfigurationContext(configuration);
        context = context.createConfigurationContext(configuration);
    }

    context.getResources().updateConfiguration(configuration,
            resources.getDisplayMetrics());
}

Chiamare il metodo precedente prima di chiamare setContentView(...) nella cartella onCreate() di tutte le attività. Il metodo locale dovrebbe essere il parametro predefinito Locale che si desidera impostare. Per esempio, se si vuole impostare l'arabo/UAE come locale predefinito, si deve passare il parametro new Locale("ar", "AE"). Oppure, se si desidera impostare il locale predefinito (cioè l'opzione Locale che viene impostato automaticamente dal sistema operativo), si dovrebbe passare Locale.US.

PASSO 2:

Inoltre, è necessario aggiungere la seguente riga di codice:

new WebView(this).destroy();

nella sezione onCreate() del vostro Application (se ne avete una) e ovunque l'utente possa cambiare la lingua. In questo modo si risolveranno tutti i tipi di casi limite che possono verificarsi al riavvio dell'applicazione dopo aver cambiato la lingua (si potrebbero notare stringhe in altre lingue o con l'allineamento opposto dopo aver cambiato la lingua su Activities che hanno WebViewsu Android 7.0++).

Come aggiunta, le schede personalizzate di Chrome sono ora il modo preferito per il rendering delle pagine web in-app.

Riferimenti:

1.Android 7.0 - modifiche per WebView.

2.Comprendere WebView e le patch di sicurezza di Android.

3.WebView per Android.

4.WebView: Da "Powered by Chrome" a Chrome puro e semplice.

5.Nougat WebView.

6.Android 7.0 Nougat.

7.Misteri di Android N, parte 1: Android System WebView ora è solo "Chrome"?.

Il codice sembra impostare il locale nella configurazione dell'applicazione stessa (MyApplication.getInstance()). Tuttavia, è necessario aggiornare la configurazione del contesto dell'attività prima di gonfiare la vista del contenuto dell'attività. Ho scoperto che modificare il contesto dell'applicazione non è sufficiente (e, come si è scoperto, non è nemmeno necessario). Se non aggiorno il contesto di ogni attività, il comportamento è incoerente tra le varie attività.

Il modo in cui affronto questo problema è quello di sottoclasse AppCompatActivity (o Activityse non si usa la libreria di compatibilità) e poi derivare tutte le mie classi di attività da quella sottoclasse. Ecco una versione semplificata del mio codice:

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.getDisplayMetrics());
        }
    }

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

Poi mi assicuro di chiamare super.onCreate(savedInstanceState) in ogni sottoclasse onCreate() di ogni metodo prima di chiamare qualsiasi metodo (come ad esempio setContentView()) che utilizzano il contesto.

Dopo aver letto tutte le risposte, ho scoperto che manca qualcosa in ognuna di esse, quindi ecco la soluzione che ha funzionato per me finora. Poiché la WebView sovrascrive la configurazione della lingua del contesto dell'attività e del contesto dell'applicazione, è necessario assicurarsi che ogni volta che ciò accade si chiami un metodo che ripristini tali modifiche. Nel mio caso ho scritto la seguente classe che estende le mie attività che presentano questo problema (quelle che mostrano una 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'idea postata da @Tobliug di salvare la configurazione iniziale prima che la WebView la sovrascriva ha funzionato per me, nel mio caso specifico ho trovato questa soluzione più facile da implementare rispetto ad altre soluzioni postate.
È importante che il metodo fix venga richiamato dopo l'uscita dalla WebView, ad esempio quando si preme indietro e in onStop.
Se la webView viene visualizzata in una finestra di dialogo, è necessario fare attenzione che il metodo fix venga richiamato dopo aver chiuso la finestra, soprattutto in onResume e/o onCreate. Se la webView viene caricata direttamente in onCreate dell'attività e non successivamente in un nuovo frammento, il metodo fix deve essere richiamato direttamente dopo setContentView, prima che venga impostato il titolo dell'attività, ecc. Se la WebView viene caricata all'interno di un frammento dell'attività, si deve chiamare l'attività in onViewCreated del frammento e l'attività deve chiamare il metodo fix.
Non è necessario che tutte le attività estendano la classe di cui sopra, come indicato in una risposta, è un eccesso e non è necessario.
Questo problema non si risolve nemmeno sostituendo la WebView con le schede di Google Chrome o aprendo un browser esterno.

Se si ha davvero bisogno che la configurazione delle risorse abbia l'intero elenco di lingue impostato e non solo una, allora è necessario unire questa soluzione con quella di https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce.
Nel mio caso non era necessario.

Inoltre non ho trovato necessario chiamare new WebView(this).destroy(); come indicato in una risposta qui.

Non dimenticare di dare visibilità a questa cronaca se ha risolto il tuo problema.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.