Zapobiegaj wyświetlaniu przez WebView komunikatu „strona internetowa niedostępna”

88

Mam aplikację, która w szerokim zakresie wykorzystuje WebView. Kiedy użytkownik tej aplikacji nie ma połączenia z Internetem, pojawia się strona z napisem „strona internetowa niedostępna” i inny tekst. Czy istnieje sposób, aby nie wyświetlać tego ogólnego tekstu w moim WebView? Chciałbym zapewnić własną obsługę błędów.

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

Dowiedziałem się, że WebView-> clearView w rzeczywistości nie czyści widoku.

JoJo
źródło
3
Dlaczego nie sprawdzisz połączenia internetowego przed wyświetleniem WebView, a jeśli nie ma dostępnego narzędzia internetowego, możesz pominąć wyświetlanie WebView i zamiast tego możesz wyświetlić alert lub toast bez wiadomości internetowej?
Andro Selva,
@JoJo czy możesz zaznaczyć odpowiedź jako poprawną? prawdopodobnie moje: P
Sherif elKhatib,

Odpowiedzi:

102

Najpierw utwórz własną stronę błędu w formacie HTML i umieść ją w folderze zasobów, nazwijmy ją myerrorpage.html Następnie za pomocą onReceivedError:

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

    }
});
SnowboardBruin
źródło
27
Stara strona błędu „Strona internetowa nie została załadowana” jest wyświetlana przez ułamek sekundy, zanim zostanie zastąpiona przez niestandardową stronę błędu z zasobów
Cheenu Madan
1
Zgadzam się z Cheenu. Musi być lepsze rozwiązanie.
Jozua,
3
1. Możesz załadować zawartość internetową, gdy WebView jest niewidoczny (i może wyświetlać zły postęp) i pokazać go, jeśli strona zostanie załadowana pomyślnie / załadować „myerrorpage.html” w przypadku błędu. 2. Należy mieć świadomość, że onReceiveError zostanie wyzwolony, jeśli wystąpią problemy z załadowaniem problemu OR w JS, który działa po załadowaniu strony (podobnie jak funkcje jQuery, które działają w zdarzeniu document.ready ()). Przeglądarka desktopowa - pozwoli na błąd JS bez powiadamiania o tym użytkownika, podobnie jak przeglądarki mobilne.
FunkSoulBrother
5
dodaj view.loadUrl("about:blank");przed loadUrl, aby zapobiec wyświetlaniu strony błędu przez ułamek sekundy.
Aashish
2
Gdzie umieścić ten plik myerrorpage.html? Gdzie znajduje się ten folder zasobów Androida?
Nived Kannada
34

Najlepszym rozwiązaniem, jakie znalazłem, jest załadowanie pustej strony w zdarzeniu OnReceivedError w następujący sposób:

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

    view.loadUrl("about:blank");
}
7heViking
źródło
Dobry pomysł! W moim przypadku dodałem view.loadUrl(noConnectionUrl);dwukrotnie (gdzie noConnectionUrl to lokalny plik z komunikatem o błędzie). Pomogło to również nie „widzieć” wbudowanej strony błędu przez ułamek sekundy.
hQuse
4
Pamiętaj, że jeśli zajmujesz się obsługą, webView.goBack()upewnij się, że obsługujesz warunek dla tej pustej strony. W przeciwnym razie wczytana zostanie poprzednia strona i jeśli ponownie się nie powiedzie, ponownie zobaczysz tę pustą stronę.
Chintan Shah
Jak więc najlepiej sobie z tym poradzić?
MoSoli
@MoSoli Wszystko zależy od Twoich potrzeb. Alternatywą dla wyświetlenia pustej strony byłoby wyświetlenie niestandardowego interfejsu użytkownika lub strony internetowej w pamięci podręcznej.
7heViking
10

Wreszcie rozwiązałem to. (Działa do teraz ..)

Moje rozwiązanie jest takie ...

  1. Przygotuj układ tak, aby zamiast strony internetowej pokazywał, kiedy wystąpił błąd (komunikat „nie znaleziono strony”) Układ ma jeden przycisk „RELOAD” z kilkoma komunikatami przewodnika.

  2. Jeśli wystąpił błąd, pamiętaj o użyciu wartości logicznych i pokaż przygotowany przez nas układ.

  3. Jeśli użytkownik kliknie przycisk „RELOAD”, ustaw parametr mbErrorOccured na false. I ustaw mbReloadPressed na true.
  4. Jeśli mbErrorOccured ma wartość false, a mbReloadPressed ma wartość true, oznacza to, że strona została załadowana pomyślnie. 'Ponieważ jeśli błąd wystąpi ponownie, mbErrorOccured zostanie ustawione na wartość true w onReceivedError (...)

Oto moje pełne źródło. Sprawdź to.

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

Dodatek: Oto układ ...

<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
źródło
Czy możesz udostępnić swój plik zasobów? Jestem nowicjuszem.
Rajan Rawal
@RajanRawal Edytowałem i dodałem przykład układu. :)
cmcromance
Nie widzę paska postępu, ale widzę tam kod. :-(
Rajan Rawal
8

Kiedy webview jest osadzony w jakimś niestandardowym widoku, tak że użytkownik prawie wierzy, że widzi widok natywny, a nie widok internetowy, w takim przypadku błąd „nie można załadować strony” jest niedorzeczny. Zwykle w takiej sytuacji ładuję pustą stronę i pokazuję toastową wiadomość jak poniżej

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
źródło
7

Sprawdź dyskusję w Android WebView onReceivedError () . Jest dość długi, ale wydaje się, że a) nie możesz zatrzymać wyświetlania strony „strona internetowa niedostępna”, ale b) zawsze możesz załadować pustą stronę po otrzymaniu onReceivedError

Torid
źródło
Ci faceci mówią o onReceivedErrortym, żeby w ogóle nie uruchamiać. Uruchamia się poprawnie w moim kodzie, gdy nie ma połączenia z Internetem.
JoJo
2

Możesz użyć GETżądania, aby pobrać zawartość strony, a następnie wyświetlić te dane za pomocą Webview, w ten sposób nie używasz wielu wywołań serwera. Alternatywnie możesz użyć Javascriptdo sprawdzenia DOMpoprawności obiektu.

Ravi Vyas
źródło
to dobry pomysł, jednak szkoda, że ​​musimy zrobić coś takiego ...
Jeffrey Blattman
2

Być może źle zrozumiałem pytanie, ale wygląda na to, że mówisz, że otrzymałeś komunikat zwrotny o błędzie i po prostu pytasz, jaki jest najlepszy sposób, aby nie pokazać błędu? Dlaczego po prostu nie usuniesz widoku internetowego z ekranu i / lub nie pokażesz na nim innego widoku?

Matthew Horst
źródło
2

Tutaj znalazłem najłatwiejsze rozwiązanie. Sprawdź to..

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

A oto metoda 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
źródło
1

Przypuszczam, że jeśli nalegasz na zrobienie tego, możesz po prostu sprawdzić, czy zasób istnieje, przed wywołaniem funkcji loadURL. Po prostu nadpisuj funkcje i sprawdź przed wywołaniem super ()

UWAGA (może nie na temat): W http znajduje się metoda o nazwie HEAD, opisana w następujący sposób:

Metoda HEAD jest identyczna z metodą GET, z wyjątkiem tego, że serwer NIE MOŻE zwracać treści wiadomości w odpowiedzi

Ta metoda może być przydatna. Tak czy inaczej, jak kiedykolwiek to zaimplementujesz ... sprawdź ten kod:

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

Możesz spróbować tego sprawdzenia za pomocą

if(url.openConnection().getContentLength() > 0)
Sherif elKhatib
źródło
6
Sprawdzenie, czy zasób istnieje, zanim faktycznie się tam przejdzie, podwoiłoby obciążenie serwera, ponieważ na 1 żądanie wirtualne przypadałyby 2 żądania fizyczne. Wydaje się to trochę przesada.
JoJo,
desperackie środki!
Sherif elKhatib,
1
Zresztą nadal można by go przeciążyć i pobrać zawartość html, sprawdzić czy nie ma w niej błędu. Jeśli nie, przeanalizuj i wyświetl to
Sherif elKhatib,
jak właściwie można to zrealizować?
JoJo,
@JoJo, w rzeczywistości ta metoda zmniejsza obciążenie serwera w przypadku nieprawidłowego adresu URL, ponieważ zwrócenie odpowiedzi HEAD ma znacznie mniejszy narzut niż zwrócenie odpowiedzi 304 lub 500.
Justin Shield,
0

Po prostu zmieniłbym stronę internetową na tę, której używasz do obsługi błędów:

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

to wszystko można znaleźć na http://developer.android.com/reference/android/webkit/WebView.html

Efraim
źródło
0

Pracowałem dzisiaj nad tym problemem porzucenia tych irytujących stron błędów Google. Jest to możliwe dzięki przykładowemu kodowi Androida widocznemu powyżej i na wielu innych forach (zapytaj, skąd wiem):

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

JEŚLI umieścisz go w shouldOverrideUrlLoading () jako jeszcze jednego klienta WWW. Przynajmniej to działa dla mnie na moim urządzeniu 2.3.6. Gdzie jeszcze to działa, zobaczymy później. Jestem pewien, że to by mnie teraz tylko przygnębiło. Kawałek goBack jest mój. Możesz tego nie chcieć.

R Earle Harris
źródło
0

Spróbuj tego

 @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
źródło
0

Możemy ustawić widoczność webView na 0 (view.INVISIBLE) i pokazać jakąś wiadomość. Ten kod działa dla mojej aplikacji działającej na Lolipopie.

@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
źródło
0

Musiałem zmierzyć się z tym problemem, a także próbowałem go rozwiązać z różnych perspektyw. Wreszcie znalazłem rozwiązanie, używając jednej flagi, aby sprawdzić, czy wystąpił błąd.

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

W ten sposób ukrywam stronę za każdym razem, gdy pojawia się błąd i pokazuję ją po ponownym załadowaniu strony.

Dodano również małe opóźnienie w przypadku.

Uniknąłem rozwiązania polegającego na wczytywaniu pustej strony, ponieważ nie pozwala na późniejsze wykonanie webview.reload (), ponieważ dodaje tę nową stronę do historii nawigacji.

Jose M. Lechon
źródło
-1

spróbuj tego shouldOverrideUrlLoading , przed przekierowaniem do innego adresu URL sprawdź, czy połączenie internetowe oparte na tej stronie powinno zostać załadowane, czy nie.

Bipin Vayalu
źródło
-1

zastąpić metodę WebViewClient

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

view.loadUrl('about:blank') ma efekty uboczne, ponieważ anuluje ładowanie oryginalnego adresu URL.

duckduckgo
źródło
view.clearView () jest przestarzała, zobacz stackoverflow.com/questions/3715482/clear-webview-content
silva96,