Impedisci a WebView di visualizzare "pagina web non disponibile"


88

Ho un'app che fa ampio uso di un WebView. Quando l'utente di questa app non dispone di una connessione Internet, viene visualizzata una pagina che dice "pagina web non disponibile" e vari altri testi. C'è un modo per non mostrare questo testo generico nel mio WebView? Vorrei fornire la mia gestione degli errori.

private final Activity activity = this;

private class MyWebViewClient extends WebViewClient
 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
  // I need to do something like this:
  activity.webView.wipeOutThePage();
  activity.myCustomErrorHandling();
  Toast.makeText(activity, description, Toast.LENGTH_LONG).show();
 }
}

Ho scoperto che WebView-> clearView in realtà non cancella la visualizzazione.


3
Perché non controlli la connessione Internet prima di mostrare WebView e se non è disponibile alcuna funzione Internet puoi saltare la visualizzazione di WebView e invece puoi mostrare un avviso o un brindisi senza messaggio Internet?
Andro Selva

@JoJo puoi contrassegnare una risposta come corretta? probabilmente il mio: P
Sherif elKhatib

Risposte:


102

Per prima cosa crea la tua pagina di errore in HTML e inseriscila nella cartella degli asset, chiamiamola myerrorpage.html Quindi con onReceivedError:

mWebView.setWebViewClient(new WebViewClient() {
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        mWebView.loadUrl("file:///android_asset/myerrorpage.html");

    }
});

27
La vecchia pagina di errore "Pagina web non caricata" viene visualizzata per una frazione di secondo prima di essere sostituita dalla pagina di errore personalizzata dalle risorse
Cheenu Madan

1
D'accordo con Cheenu. Deve esserci una soluzione migliore.
Jozua

3
1. È possibile caricare il contenuto web mentre il WebView è invisibile (e forse visualizzare un progresso errato su di esso) e mostrarlo se la pagina è stata caricata correttamente / caricare "myerrorpage.html" in caso di errore. 2. Dovresti essere consapevole che onReceiveError verrà attivato se ci sono problemi nel caricamento del problema OR in JS che viene eseguito dopo che la pagina è stata caricata (come le funzioni jQuery che vengono eseguite nell'evento document.ready ()). Browser desktop: consentirà l'errore JS senza avvisare l'utente, come dovrebbero fare i browser mobili.
FunkSoulBrother

5
aggiungi view.loadUrl("about:blank");prima di loadUrl per evitare che mostri la pagina di errore per una frazione di secondo.
Aashish

2
Dove mettere il file myerrorpage.html? Dove si trova la cartella delle risorse Android?
Nived Kannada

34

La soluzione migliore che ho trovato è caricare una pagina vuota nell'evento OnReceivedError in questo modo:

@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);

    view.loadUrl("about:blank");
}

Buona idea! Nel mio caso ho aggiunto view.loadUrl(noConnectionUrl);due volte (dove noConnectionUrl è il file locale con messaggio di errore). Ciò ha anche aiutato a non "vedere" la pagina di errore incorporata per una frazione di secondo.
hQuse

4
Tieni presente che, se stai gestendo, webView.goBack()assicurati di gestire le condizioni per questa pagina vuota. Altrimenti la tua pagina precedente verrà caricata e se fallisce di nuovo, vedrai di nuovo questa pagina vuota.
Chintan Shah

Allora, qual è il modo migliore per gestirlo?
MoSoli

@MoSoli Dipende tutto dalle tue esigenze. Un'alternativa alla visualizzazione di una pagina vuota sarebbe quella di mostrare un'interfaccia utente personalizzata o una pagina Web memorizzata nella cache.
7heViking

10

Alla fine ho risolto questo problema. (Funziona fino ad ora ..)

La mia soluzione è così ...

  1. Prepara il layout per mostrare quando si è verificato un errore al posto della pagina Web (un sporco 'messaggio di pagina non trovata') Il layout ha un pulsante, "RELOAD" con alcuni messaggi guida.

  2. Se si è verificato un errore, ricorda di utilizzare booleano e mostra il layout che prepariamo.

  3. Se l'utente fa clic sul pulsante "RELOAD", imposta mbErrorOccured su false. E imposta mbReloadPressed su true.
  4. se mbErrorOccured è false e mbReloadPressed è true, significa che la pagina webview è stata caricata correttamente. Perché se l'errore si è verificato di nuovo, mbErrorOccured verrà impostato su true su onReceivedError (...)

Ecco la mia fonte completa. Controllalo.

public class MyWebViewActivity extends ActionBarActivity implements OnClickListener {

    private final String TAG = MyWebViewActivity.class.getSimpleName();
    private WebView mWebView = null;
    private final String URL = "http://www.google.com";
    private LinearLayout mlLayoutRequestError = null;
    private Handler mhErrorLayoutHide = null;

    private boolean mbErrorOccured = false;
    private boolean mbReloadPressed = false;

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);

        ((Button) findViewById(R.id.btnRetry)).setOnClickListener(this);
        mlLayoutRequestError = (LinearLayout) findViewById(R.id.lLayoutRequestError);
        mhErrorLayoutHide = getErrorLayoutHideHandler();

        mWebView = (WebView) findViewById(R.id.webviewMain);
        mWebView.setWebViewClient(new MyWebViewClient());
        WebSettings settings = mWebView.getSettings();
        settings.setJavaScriptEnabled(true);
        mWebView.setWebChromeClient(getChromeClient());
        mWebView.loadUrl(URL);
    }

    @Override
    public boolean onSupportNavigateUp() {
        return super.onSupportNavigateUp();
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();

        if (id == R.id.btnRetry) {
            if (!mbErrorOccured) {
                return;
            }

            mbReloadPressed = true;
            mWebView.reload();
            mbErrorOccured = false;
        }
    }

    @Override
    public void onBackPressed() {
        if (mWebView.canGoBack()) {
            mWebView.goBack();
            return;
        }
        else {
            finish();
        }

        super.onBackPressed();
    }

    class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            return super.shouldOverrideUrlLoading(view, url);
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }

        @Override
        public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            if (mbErrorOccured == false && mbReloadPressed) {
                hideErrorLayout();
                mbReloadPressed = false;
            }

            super.onPageFinished(view, url);
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            mbErrorOccured = true;
            showErrorLayout();
            super.onReceivedError(view, errorCode, description, failingUrl);
        }
    }

    private WebChromeClient getChromeClient() {
        final ProgressDialog progressDialog = new ProgressDialog(MyWebViewActivity.this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(false);

        return new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
            }
        };
    }

    private void showErrorLayout() {
        mlLayoutRequestError.setVisibility(View.VISIBLE);
    }

    private void hideErrorLayout() {
        mhErrorLayoutHide.sendEmptyMessageDelayed(10000, 200);
    }

    private Handler getErrorLayoutHideHandler() {
        return new Handler() {
            @Override
            public void handleMessage(Message msg) {
                mlLayoutRequestError.setVisibility(View.GONE);
                super.handleMessage(msg);
            }
        };
    }
}

Aggiunta: ecco il layout ...

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rLayoutWithWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<WebView
    android:id="@+id/webviewMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<LinearLayout
    android:id="@+id/lLayoutRequestError"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"
    android:background="@color/white"
    android:gravity="center"
    android:orientation="vertical"
    android:visibility="gone" >

    <Button
        android:id="@+id/btnRetry"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minWidth="120dp"
        android:text="RELOAD"
        android:textSize="20dp"
        android:textStyle="bold" />
</LinearLayout>


Puoi condividere il tuo file di risorse? Sono un principiante.
Rajan Rawal

@RajanRawal Ho modificato e aggiunto l'esempio di layout. :)
cmcromance

Non riesco a vedere alcuna barra di avanzamento, ma posso vedere il codice lì. :-(
Rajan Rawal

7

Guarda la discussione su Android WebView onReceivedError () . È piuttosto lungo, ma il consenso sembra essere che a) non puoi impedire la visualizzazione della pagina "pagina web non disponibile", ma b) potresti sempre caricare una pagina vuota dopo aver ricevuto un errore onReceivedError


Questi ragazzi stanno parlando di onReceivedErrornon innescare affatto. Si attiva correttamente nel mio codice quando non c'è connessione a Internet.
JoJo

7

Quando la visualizzazione web è incorporata in una visualizzazione personalizzata in modo tale che l'utente quasi crede di vedere una visualizzazione nativa e non una visualizzazione web, in tale scenario che mostra l'errore "impossibile caricare la pagina" è assurdo. Quello che faccio di solito in tale situazione è caricare una pagina vuota e visualizzare un messaggio di avviso come di seguito

webView.setWebViewClient(new WebViewClient() {

            @Override
            public void onReceivedError(WebView view, int errorCode,
                    String description, String failingUrl) {
                Log.e(TAG," Error occured while loading the web page at Url"+ failingUrl+"." +description);     
                view.loadUrl("about:blank");
                Toast.makeText(App.getContext(), "Error occured, please check newtwork connectivity", Toast.LENGTH_SHORT).show();
                super.onReceivedError(view, errorCode, description, failingUrl);
            }
        });

2

È possibile utilizzare una GETrichiesta per ottenere il contenuto della pagina e quindi visualizzare i dati utilizzando Webview, in questo modo non si utilizzano più chiamate al server. Alternativa che puoi utilizzare Javascriptper verificare la DOMvalidità dell'oggetto.


è una buona idea, tuttavia, è davvero un peccato che dobbiamo fare qualcosa del genere ...
Jeffrey Blattman

2

Forse fraintendo la domanda, ma sembra che tu stia dicendo che hai ricevuto l'errore di richiamata e ti stai solo chiedendo qual è il modo migliore per non mostrare l'errore? Perché non rimuovi semplicemente la visualizzazione Web dallo schermo e / o mostra un'altra visualizzazione sopra di essa?


2

Qui ho trovato la soluzione più semplice. controlla questo..

   @Override
        public void onReceivedError(WebView view, int errorCode,
                                    String description, String failingUrl) {
//                view.loadUrl("about:blank");
            mWebView.stopLoading();
            if (!mUtils.isInterentConnection()) {
                Toast.makeText(ReportingActivity.this, "Please Check Internet Connection!", Toast.LENGTH_SHORT).show();
            }
            super.onReceivedError(view, errorCode, description, failingUrl);
        }

Ed ecco il metodo InterentConnection () ...

public boolean isInterentConnection() {
    ConnectivityManager manager = (ConnectivityManager) mContext
            .getSystemService(Context.CONNECTIVITY_SERVICE);
    if (manager != null) {
        NetworkInfo info[] = manager.getAllNetworkInfo();
        if (info != null) {
            for (int i = 0; i < info.length; i++) {
                if (info[i].getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
    }
    return false;
}

1

Suppongo che se insisti a farlo, potresti semplicemente controllare se la risorsa è presente prima di chiamare la funzione loadURL. Basta semplicemente sovrascrivere le funzioni ed eseguire il controllo prima di chiamare super ()

NOTA (forse fuori tema): In http, c'è un metodo chiamato HEAD che è descritto come segue:

Il metodo HEAD è identico a GET tranne per il fatto che il server NON DEVE restituire il corpo del messaggio nella risposta

Questo metodo potrebbe essere utile. Comunque come mai lo implementerai ... controlla questo codice:

import java.util.Map;

import android.content.Context;
import android.webkit.WebView;

public class WebViewPreLoad extends WebView{

public WebViewPreLoad(Context context) {
super(context);
}
public void loadUrl(String str){
if(//Check if exists)
super.loadUrl(str);
else
//handle error
}
public void loadUrl(String url, Map<String,String> extraHeaders){
if(//Check if exists)
super.loadUrl(url, extraHeaders);
else
//handle error
}
}

Puoi provare questo controllo usando

if(url.openConnection().getContentLength() > 0)

6
Controllare se la risorsa è presente prima di andare effettivamente lì raddoppierebbe il carico sul server perché ci sarebbero 2 richieste fisiche per 1 richiesta virtuale. Questo sembra un po 'eccessivo.
JoJo

ben misure disperate!
Sherif elKhatib

1
Ad ogni modo potresti comunque sovraccaricarlo e ottenere il contenuto html, controlla se ha un errore. In caso contrario, analizzalo e visualizzalo
Sherif elKhatib

come si implementerebbe effettivamente questo?
JoJo

@JoJo, in realtà questo metodo riduce il carico sul server in caso di un URL non valido poiché la restituzione di una risposta HEAD ha un overhead molto inferiore rispetto alla restituzione di una risposta 304 o 500.
Justin Shield

0

Vorrei solo modificare la pagina Web in qualsiasi cosa tu stia utilizzando per la gestione degli errori:

getWindow().requestFeature(Window.FEATURE_PROGRESS);  
webview.getSettings().setJavaScriptEnabled(true);  
final Activity activity = this;  
webview.setWebChromeClient(new WebChromeClient() {  
public void onProgressChanged(WebView view, int progress) {  
 // Activities and WebViews measure progress with different scales.  
 // The progress meter will automatically disappear when we reach 100%  
 activity.setProgress(progress * 1000);  
 }  
});  
webview.setWebViewClient(new WebViewClient() {  
public void onReceivedError(WebView view, int errorCode, String description, String 
failingUrl) {  
 Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();  
}  
});  
webview.loadUrl("http://slashdot.org/");

tutto questo può essere trovato su http://developer.android.com/reference/android/webkit/WebView.html


0

Ho lavorato su questo problema di abbandonare quelle irritabili pagine di errore di Google oggi. È possibile con il codice di esempio Android visto sopra e in molti altri forum (chiedi come lo so):

wv.setWebViewClient(new WebViewClient() {
    public void onReceivedError(WebView view, int errorCode,
                            String description, String failingUrl) {
       if (view.canGoBack()) {
          view.goBack();
        }
       Toast.makeText(getBaseContext(), description, Toast.LENGTH_LONG).show();
       }
    }
});

SE lo metti in shouldOverrideUrlLoading () come un client web in più. Almeno, questo funziona per me sul mio dispositivo 2.3.6. Vedremo dove altro funziona più tardi. Questo mi deprimerebbe solo adesso, ne sono sicuro. Il bit goBack è mio. Potresti non volerlo.


0

Prova questo

 @SuppressWarnings("deprecation")
 @Override
 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
      // Your handling
 }

 @Override
 public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
      }
 }

0

Possiamo impostare la visibilità di webView a 0 (view.INVISIBLE) e mostrare qualche messaggio. Questo codice funziona per la mia app in esecuzione su lolipop.

@SuppressWarnings("deprecation")
    @Override
    public void onReceivedError(WebView webView, int errorCode, String description, String failingUrl) {
        // hide webView content and show custom msg
        webView.setVisibility(View.INVISIBLE);
        Toast.makeText(NrumWebViewActivity.this,getString(R.string.server_not_responding), Toast.LENGTH_SHORT).show();
    }

    @TargetApi(android.os.Build.VERSION_CODES.M)
    @Override
    public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
        // Redirect to deprecated method, so you can use it in all SDK versions
        onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
    }

0

Ho dovuto affrontare questo problema e ho anche cercato di risolverlo da diverse prospettive. Finalmente ho trovato una soluzione utilizzando un unico flag per verificare se si è verificato un errore.

... extends WebViewClient {

    boolean error;

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {

        showLoading(true);
        super.onPageStarted(view, url, favicon);

        error  = false; // IMPORTANT
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);

        if(!error) {
            Observable.timer(100, TimeUnit.MICROSECONDS, AndroidSchedulers.mainThread())
                    .subscribe((data) -> view.setVisibility(View.VISIBLE) );
        }

        showLoading(false);
    }


    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

        view.stopLoading();

        view.setVisibility(View.INVISIBLE) 
        error  = true;  

        // Handle the error
    }


     @Override
    @TargetApi(android.os.Build.VERSION_CODES.M)
    public void onReceivedError(WebView view,
                                WebResourceRequest request,
                                WebResourceError error) {

        this.onReceivedError(view, error.getErrorCode(),
                error.getDescription().toString(),
                request.getUrl().toString());
    }
 }

In questo modo nascondo la pagina ogni volta che c'è un errore e la mostro quando la pagina è stata caricata di nuovo correttamente.

Aggiunto anche un piccolo ritardo nel caso.

Ho evitato la soluzione di caricare una pagina vuota in quanto non ti consente di fare webview.reload () in seguito perché aggiunge quella nuova pagina nella cronologia di navigazione.



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.