Evitar que WebView muestre "página web no disponible"

88

Tengo una aplicación que hace un uso extensivo de WebView. Cuando el usuario de esta aplicación no tiene conexión a Internet, aparece una página que dice "página web no disponible" y varios otros textos. ¿Hay alguna forma de no mostrar este texto genérico en mi WebView? Me gustaría proporcionar mi propio manejo de errores.

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

Descubrí que WebView-> clearView en realidad no borra la vista.

JoJo
fuente
3
¿Por qué no comprueba la conexión a Internet antes de mostrar webView y, si no hay una instalación de Internet disponible, puede omitir la visualización de WebView y, en su lugar, puede mostrar una alerta o brindis sin mensaje de Internet?
Andro Selva
@JoJo, ¿puedes marcar una respuesta como correcta? probablemente mío: P
Sherif elKhatib

Respuestas:

102

Primero cree su propia página de error en HTML y colóquela en su carpeta de activos, llamémosla myerrorpage.html Luego con onReceivedError:

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

    }
});
SnowboardBruina
fuente
27
La antigua página de error "Página web no cargada" se muestra durante una fracción de segundo antes de ser reemplazada por la página de error personalizada de los activos
Cheenu Madan
1
De acuerdo con Cheenu. Tiene que haber una solución mejor.
Jozua
3
1. Puede cargar el contenido web mientras WebView es invisible (y tal vez mostrar un mal progreso sobre él) y mostrarlo si la página se carga correctamente / cargar "myerrorpage.html" en caso de error. 2. Debe tener en cuenta que onReceiveError se activará si hay problemas al cargar el problema OR en JS que se ejecuta después de que se cargó la página (como las funciones jQuery que se ejecutan en el evento document.ready ()). Navegador de escritorio: permitirá el error JS sin notificar al usuario al respecto, como deberían hacerlo los navegadores móviles.
FunkSoulBrother
5
agregue view.loadUrl("about:blank");antes de loadUrl para evitar que muestre la página de error durante una fracción de segundo.
Aashish
2
¿Dónde poner ese archivo myerrorpage.html? ¿Dónde se encuentra esa carpeta de activos de Android?
Publicado en Kannada
34

La mejor solución que he encontrado es cargar una página vacía en el evento OnReceivedError como este:

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

    view.loadUrl("about:blank");
}
7heViking
fuente
¡Buena idea! En mi caso, agregué view.loadUrl(noConnectionUrl);dos veces (donde noConnectionUrl es un archivo local con un mensaje de error). Eso también ayudó a no "ver" la página de error incorporada durante una fracción de segundo.
hQuse
4
Tenga en cuenta que, si está manipulando webView.goBack(), asegúrese de gestionar la condición de esta página en blanco. De lo contrario, se cargará su página anterior y, si falla nuevamente, verá esta página en blanco nuevamente.
Chintan Shah
Entonces, ¿cuál es la mejor manera de manejarlo?
MoSoli
@MoSoli Todo depende de tus necesidades. Una alternativa a mostrar una página vacía sería mostrar una interfaz de usuario personalizada o una página web almacenada en caché.
7heViking
10

Finalmente, resolví esto. (Funciona hasta ahora ..)

Mi solución es así ...

  1. Prepare el diseño para mostrar cuando ocurrió un error en lugar de la página web (un 'mensaje de página no encontrada' sucio) El diseño tiene un botón, "RELOAD" con algunos mensajes de guía.

  2. Si ocurrió un error, recuerde usar boolean y mostrar el diseño que preparamos.

  3. Si el usuario hace clic en el botón "RECARGAR", establezca mbErrorOccured en falso. Y establezca mbReloadPressed en verdadero.
  4. si mbErrorOccured es falso y mbReloadPressed es verdadero, significa que la página webview se cargó correctamente. Porque si el error ocurre nuevamente, mbErrorOccured se establecerá como verdadero en onReceivedError (...)

Aquí está mi fuente completa. Mira esto.

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

Adición: Aquí está el diseño ...

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

cmcromance
fuente
¿Puede compartir su archivo de recursos? Soy novato
Rajan Rawal
@RajanRawal Edité y agregué el ejemplo de diseño. :)
cmcromance
No puedo ver ninguna barra de progreso, pero puedo ver el código allí. :-(
Rajan Rawal
8

Cuando la vista web está incrustada en alguna vista personalizada de modo que el usuario casi cree que está viendo una vista nativa y no una vista web, en tal escenario, mostrar el error "la página no se pudo cargar" es absurdo. Lo que suelo hacer en tal situación es cargar una página en blanco y mostrar un mensaje de brindis como se muestra a continuación

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);
            }
        });
Jeevan
fuente
7

Consulte la discusión en Android WebView onReceivedError () . Es bastante largo, pero el consenso parece ser que a) no se puede evitar que aparezca la página "página web no disponible", pero b) siempre se puede cargar una página vacía después de recibir un error onReceivedError

Librar
fuente
Estos chicos están hablando de onReceivedErrorno disparar en absoluto. Se activa correctamente en mi código cuando no hay conexión a Internet.
JoJo
2

Puede usar una GETsolicitud para obtener el contenido de la página y luego mostrar esos datos usando el Webview, de esta manera no está usando múltiples llamadas al servidor. Alternativa que puede utilizar Javascriptpara comprobar la DOMvalidez del objeto.

Ravi Vyas
fuente
eso es una buena idea, sin embargo, es realmente lamentable que tenemos que hacer algo como esto ...
Jeffrey Blattman
2

Tal vez no entiendo la pregunta, pero parece que está diciendo que recibe la devolución de llamada del error recibido, y solo pregunta cuál es la mejor manera de no mostrar el error. ¿Por qué no elimina la vista web de la pantalla y / o muestra otra vista encima?

Matthew Horst
fuente
2

Aquí encontré la solución más sencilla. mira esto..

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

Y aquí está el método isInterentConnection () ...

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;
}
Ankush Joshi
fuente
1

Supongo que si insiste en hacer esto, puede verificar si el recurso está allí antes de llamar a la función loadURL. Simplemente anule las funciones y haga la verificación antes de llamar al super ()

OBSERVACIÓN (quizás fuera del tema): En http, hay un método llamado HEAD que se describe a continuación:

El método HEAD es idéntico a GET, excepto que el servidor NO DEBE devolver un cuerpo de mensaje en la respuesta

Este método puede resultar útil. De todos modos, como sea que lo implemente ... verifique este código:

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

Podrías probar esta verificación usando

if(url.openConnection().getContentLength() > 0)
Sherif elKhatib
fuente
6
Verificar si el recurso está allí antes de ir allí duplicaría la carga en el servidor porque habría 2 solicitudes físicas por 1 solicitud virtual. Esto parece un poco exagerado.
JoJo
bien medidas desesperadas!
Sherif elKhatib
1
De todos modos, aún podría sobrecargarlo y obtener el contenido html, verifique si tiene un error. Si no, analizar y mostrarlo
Sherif elKhatib
¿Cómo se implementaría realmente esto?
JoJo
@JoJo, en realidad, este método reduce la carga en el servidor en caso de una URL no válida, ya que devolver una respuesta HEAD tiene una sobrecarga mucho menor que devolver una respuesta 304 o 500.
Justin Shield
0

Simplemente cambiaría la página web a lo que esté usando para el manejo de errores:

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

todo esto se puede encontrar en http://developer.android.com/reference/android/webkit/WebView.html

Efraín
fuente
0

Hoy he estado trabajando en este problema de deshacerme de esas irritables páginas de error de Google. Es posible con el código de ejemplo de Android que se ve arriba y en muchos otros foros (pregunte cómo lo sé):

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

SI lo coloca en shouldOverrideUrlLoading () como un cliente web más. Al menos, esto me funciona en mi dispositivo 2.3.6. Veremos dónde más funciona más tarde. Eso solo me deprimiría ahora, estoy seguro. El bit goBack es mío. Puede que no lo desee.

R Earle Harris
fuente
0

Prueba esto

 @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());
      }
 }
Virat18
fuente
0

Podemos establecer la visibilidad de webView en 0 (view.INVISIBLE) y mostrar algún mensaje. Este código funciona para mi aplicación que se ejecuta en 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());
    }
Mahen
fuente
0

He tenido que afrontar este problema y también he tratado de solucionarlo desde diferentes perspectivas. Finalmente encontré una solución usando una sola bandera para verificar si ocurrió un error.

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

De esta forma, oculto la página cada vez que hay un error y lo muestro cuando la página se ha cargado de nuevo correctamente.

También se agregó un pequeño retraso por si acaso.

Evité la solución de cargar una página vacía ya que no le permite hacer webview.reload () más adelante debido a que agrega esa nueva página en el historial de navegación.

José M Lechón
fuente
-1

intente esto shouldOverrideUrlLoading , antes de redirigir a otra URL, verifique si la conexión a Internet basada en esa página debe cargarse o no.

Bipin Vayalu
fuente
-1

anular su método WebViewClient

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

view.loadUrl('about:blank') tiene efectos secundarios, ya que cancela la carga de la URL original.

Pato Pato a ganar
fuente
view.clearView () está en desuso, consulte stackoverflow.com/questions/3715482/clear-webview-content
silva96