Impedir que o WebView exiba "página da web não disponível"

88

Eu tenho um aplicativo que faz uso extensivo de um WebView. Quando o usuário deste aplicativo não possui conexão com a Internet, aparece uma página dizendo "página da web não disponível" e vários outros textos. Existe uma maneira de não mostrar este texto genérico no meu WebView? Eu gostaria de fornecer meu próprio tratamento de erros.

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

Eu descobri que WebView-> clearView não limpa a visualização.

JoJo
fonte
3
Por que você não verifica a conexão com a Internet antes de exibir o webView e, se não houver nenhum recurso de Internet disponível, você pode pular a exibição do WebView e, em vez disso, pode mostrar um alerta ou brinde sem mensagem da Internet?
Andro Selva
@JoJo você pode marcar uma resposta como correta? provavelmente meu: P
Sherif elKhatib

Respostas:

102

Primeiro crie sua própria página de erro em HTML e coloque-a na pasta de ativos, vamos chamá-la de myerrorpage.html Em seguida, com onReceivedError:

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

    }
});
SnowboardBruin
fonte
27
A página de erro "Página da Web não carregada" antiga é exibida por uma fração de segundo antes de ser substituída pela página de erro personalizada dos recursos
Cheenu Madan,
1
Concordo com Cheenu. Tem que haver uma solução melhor.
Jozua
3
1. Você pode carregar o conteúdo da web enquanto o WebView está invisível (e talvez exibir um progresso ruim sobre ele) e mostrar se a página foi carregada com êxito / carregar "myerrorpage.html" em caso de erro. 2. Você deve estar ciente de que onReceiveError será acionado se houver problemas ao carregar o OR no JS que é executado após o carregamento da página (como funções jQuery que são executadas no evento document.ready ()). Navegador de desktop - permitirá o erro JS sem notificar o usuário sobre isso, como os navegadores móveis devem fazer.
FunkSoulBrother
5
adicione view.loadUrl("about:blank");antes de loadUrl para evitar que ele mostre a página de erro por uma fração de segundo.
Aashish
2
Onde colocar esse arquivo myerrorpage.html? Onde essa pasta de ativos do Android está localizada?
Nived Kannada
34

A melhor solução que encontrei é carregar uma página vazia no 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
fonte
Boa ideia! No meu caso, adicionei view.loadUrl(noConnectionUrl);duas vezes (onde noConnectionUrl é o arquivo local com mensagem de erro). Isso também ajudou a não "ver" a página de erro embutida por uma fração de segundo.
hQuse
4
Observe que, se você estiver manipulando webView.goBack(), certifique-se de manipular a condição para esta página em branco. Caso contrário, sua página anterior será carregada e se falhar novamente, você verá esta página em branco novamente.
Chintan Shah
Então, qual é a melhor maneira de lidar com isso?
MoSoli
@MoSoli Tudo depende das suas necessidades. Uma alternativa para exibir uma página vazia seria mostrar alguma interface do usuário personalizada ou uma página da web em cache.
7heViking
10

Finalmente, resolvi isso. (Funciona até agora ..)

Minha solução é assim ...

  1. Prepare o layout para mostrar quando ocorreu um erro em vez da página da Web (uma mensagem suja de 'página não encontrada'). O layout possui um botão, "RELOAD" com algumas mensagens de guia.

  2. Se ocorrer um erro, lembre-se de usar booleano e mostre o layout que preparamos.

  3. Se o usuário clicar no botão "RELOAD", defina mbErrorOccured como false. E defina mbReloadPressed como true.
  4. se mbErrorOccured for false e mbReloadPressed for true, significa que o webview carregou a página com sucesso. Porque se o erro ocorrer novamente, mbErrorOccured será definido como verdadeiro em onReceivedError (...)

Aqui está minha fonte completa. Veja isso.

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

Adição: aqui está o 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>

cmcromance
fonte
Você pode compartilhar seu arquivo de recurso? Eu sou novato
Rajan Rawal
@RajanRawal editei e adicionei o exemplo de layout. :)
cmcromance
Não consigo ver nenhuma barra de progresso, mas consigo ver o código lá. :-(
Rajan Rawal
7

Confira a discussão em Android WebView onReceivedError () . É bastante longo, mas o consenso parece ser que a) você não pode impedir que a página "página da web não disponível" apareça, mas b) você sempre pode carregar uma página vazia depois de obter um onReceivedError

Torid
fonte
Esses caras estão falando sobre onReceivedErrornão disparar. Ele está disparando corretamente no meu código quando não há conexão com a Internet.
JoJo
7

Quando o webview está embutido em alguma visualização customizada de tal forma que o usuário quase acredita que está vendo uma visualização nativa e não uma webview, em tal cenário, mostrar o erro "a página não pôde ser carregada" é absurdo. O que eu geralmente faço nessa situação é carregar uma página em branco e mostrar uma mensagem de brinde como abaixo

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

Você pode usar uma GETsolicitação para obter o conteúdo da página e, em seguida, exibir esses dados usando o Webview, desta forma, você não está usando várias chamadas de servidor. Alternativa que você pode usar Javascriptpara verificar a DOMvalidade do objeto.

Ravi Vyas
fonte
é uma boa ideia, no entanto, é realmente uma pena que precisamos fazer algo assim ...
Jeffrey Blattman
2

Talvez eu não tenha entendido a pergunta, mas parece que você está dizendo que obtém o retorno de chamada de erro recebido e está apenas perguntando qual é a melhor maneira de não mostrar o erro? Por que você simplesmente não remove a visualização da web da tela e / ou mostra outra visualização em cima dela?

Matthew Horst
fonte
2

Aqui encontrei a solução mais fácil. Veja isso..

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

E aqui está o 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
fonte
1

Suponho que, se você insistir em fazer isso, poderá apenas verificar se o recurso está lá antes de chamar a função loadURL. Simplesmente substitua as funções e faça a verificação antes de chamar o super ()

OBSERVAÇÃO (talvez fora do tópico): Em http, há um método chamado HEAD que é descrito a seguir:

O método HEAD é idêntico ao GET, exceto que o servidor NÃO DEVE retornar um corpo de mensagem na resposta

Este método pode ser útil. De qualquer forma, você o implementa ... 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
}
}

Você pode tentar esta verificação usando

if(url.openConnection().getContentLength() > 0)
Sherif elKhatib
fonte
6
Verificar se o recurso está lá antes de realmente ir para lá dobraria a carga no servidor, pois haveria 2 solicitações físicas por 1 solicitação virtual. Isso parece um pouco exagero.
JoJo
bem medidas desesperadas!
Sherif elKhatib de
1
De qualquer forma você ainda pode sobrecarregá-lo e pegar o conteúdo html ,, verifique se tem algum erro. Se não, analise e exiba
Sherif elKhatib de
como alguém realmente implementaria isso?
JoJo
@JoJo, na verdade, esse método reduz a carga no servidor no caso de um url inválido, pois o retorno de uma resposta HEAD tem uma sobrecarga muito menor do que o retorno de uma resposta 304 ou 500.
Justin Shield
0

Gostaria apenas de alterar a página da web para o que você está usando para tratamento de erros:

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

tudo isso pode ser encontrado em http://developer.android.com/reference/android/webkit/WebView.html

Efraim
fonte
0

Tenho trabalhado neste problema de me livrar dessas irritáveis ​​páginas de erro do Google hoje. É possível com o código de exemplo do Android visto acima e em muitos outros fóruns (pergunte como eu sei):

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 você colocá-lo em shouldOverrideUrlLoading () como mais um cliente da web. Pelo menos, isso está funcionando para mim no meu dispositivo 2.3.6. Veremos onde mais funciona mais tarde. Isso só me deprimia agora, tenho certeza. A parte goBack é minha. Você pode não querer.

R Earle Harris
fonte
0

Tente isto

 @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
fonte
0

Podemos definir a visibilidade de webView para 0 (view.INVISIBLE) e mostrar alguma mensagem. Este código funciona para meu aplicativo em execução no 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
fonte
0

Tive que enfrentar esse problema e também tentei resolvê-lo de diferentes perspectivas. Finalmente encontrei uma solução usando um único sinalizador para verificar se ocorreu um erro.

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

Desta forma, oculto a página sempre que há um erro e mostro-o quando a página é carregada novamente de forma adequada.

Também adicionou um pequeno atraso no caso.

Evitei a solução de carregar uma página vazia, pois não permite que você faça webview.reload () mais tarde, pois adiciona essa nova página no histórico de navegação.

Jose M Lechon
fonte
-1

tente isso shouldOverrideUrlLoading , antes de redirecionar para outro url, verifique se a conexão com a Internet com base na página deve ser carregada ou não.

Bipin Vayalu
fonte
-1

substituir seu método WebViewClient

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

view.loadUrl('about:blank') tem efeitos colaterais, pois cancela o carregamento do URL original.

duckduckgo
fonte
view.clearView () está obsoleto, consulte stackoverflow.com/questions/3715482/clear-webview-content
silva96