Запретить WebView отображать «веб-страница недоступна»


88

У меня есть приложение, в котором широко используется WebView. Когда у пользователя этого приложения нет подключения к Интернету, появляется страница с надписью «веб-страница недоступна» и другой различный текст. Есть ли способ не отображать этот общий текст в моем WebView? Я хотел бы предоставить свою собственную обработку ошибок.

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

Я обнаружил, что WebView-> clearView на самом деле не очищает представление.


3
Почему бы вам не проверить подключение к Интернету перед показом webView, и если Интернет недоступен, вы можете пропустить отображение WebView и вместо этого вы можете показать предупреждение или тост без интернет-сообщения?
Андро Сельва

@JoJo, можешь отметить ответ как правильный? наверное мой: P
Sherif elKhatib

Ответы:


102

Сначала создайте свою собственную страницу ошибки в HTML и поместите ее в папку с ресурсами, назовем ее myerrorpage.html Затем с onReceivedError:

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

    }
});

27
Старая страница ошибки «Веб-страница не загружена» отображается в течение доли секунды, а затем заменяется настраиваемой страницей ошибки из ресурсов
Чину Мадан

1
Согласен с Чину. Должно быть лучшее решение.
Jozua

3
1. Вы можете загрузить веб-контент, пока WebView невидим (и, возможно, отображать плохой прогресс над ним), и показать его, если страница загружена успешно / загрузить "myerrorpage.html" в случае ошибки. 2. Вы должны знать, что onReceiveError будет срабатывать, если есть проблемы с загрузкой задачи ИЛИ в JS, которая запускается после загрузки страницы (например, функции jQuery, которые запускаются в событии document.ready ()). Настольный браузер - разрешит ошибку JS, не уведомляя об этом пользователя, как и мобильные браузеры.
FunkSoulBrother

5
добавьте view.loadUrl("about:blank");перед loadUrl, чтобы предотвратить отображение страницы с ошибкой в ​​течение доли секунды.
Aashish

2
Куда поместить этот файл myerrorpage.html? Где находится папка с ресурсами Android?
Нивед Каннада

34

Лучшее решение, которое я нашел, - загрузить пустую страницу в событии OnReceivedError следующим образом:

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

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

Хорошая идея! В моем случае я добавил view.loadUrl(noConnectionUrl);дважды (где noConnectionUrl - это локальный файл с сообщением об ошибке). Это также помогло не «видеть» встроенную страницу с ошибкой в ​​течение доли секунды.
hQuse

4
Обратите внимание, что если вы обрабатываете, webView.goBack()убедитесь, что обработали условие для этой пустой страницы. В противном случае будет загружена ваша предыдущая страница, и если снова произойдет сбой, вы снова увидите эту пустую страницу.
Chintan Shah 06

Итак, как лучше всего справиться с этим?
MoSoli

@MoSoli Все зависит от ваших потребностей. Альтернативой отображению пустой страницы может быть отображение пользовательского интерфейса или кешированной веб-страницы.
7heViking

10

Наконец, я решил это. (Работает до сих пор ..)

Мое решение такое ...

  1. Подготовьте макет для отображения, когда произошла ошибка вместо веб-страницы (грязное сообщение «страница не найдена»). В макете есть одна кнопка «ПЕРЕЗАГРУЗИТЬ» с некоторыми сообщениями-подсказками.

  2. Если произошла ошибка, не забудьте использовать логическое значение и показать подготовленный макет.

  3. Если пользователь нажимает кнопку «ПЕРЕЗАГРУЗИТЬ», установите для mbErrorOccured значение false. И установите для mbReloadPressed значение true.
  4. Если mbErrorOccured имеет значение false, а mbReloadPressed - true, это означает, что веб-просмотр успешно загружен. Потому что, если ошибка возникла снова, mbErrorOccured будет установлено true на onReceivedError (...)

Вот мой полный исходник. Проверь это.

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

Дополнение: Вот макет ....

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


Не могли бы вы поделиться своим файлом ресурсов? Я новичок.
Rajan Rawal

@RajanRawal Я отредактировал и добавил пример верстки. :)
cmcromance

Я не вижу индикатора выполнения, но вижу там код. :-(
Раджан Равал

8

Когда веб-просмотр встроен в какое-то настраиваемое представление, так что пользователь почти верит, что он видит собственное представление, а не веб-просмотр, в таком сценарии, показывающая ошибку «страница не может быть загружена», является нелепым. Что я обычно делаю в такой ситуации, так это загружаю пустую страницу и показываю тост, как показано ниже.

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

7

Ознакомьтесь с обсуждением onReceivedError () в Android WebView . Это довольно долго, но, похоже, консенсус состоит в том, что а) вы не можете остановить появление страницы «веб-страница недоступна», но б) вы всегда можете загрузить пустую страницу после получения ошибки onReceivedError


Эти парни говорят о том, чтобы onReceivedErrorвообще не срабатывать. Он правильно запускается в моем коде, когда нет подключения к Интернету.
JoJo

2

Вы можете использовать GETзапрос для получения содержимого страницы, а затем отображать эти данные с помощью Webview, таким образом вы не используете несколько вызовов сервера. Альтернативу можно использовать Javascriptдля проверки DOMобъекта на предмет действительности.


это хорошая идея, однако очень жаль, что нам нужно сделать что-то подобное ...
Джеффри Блаттман,

2

Возможно, я неправильно понимаю вопрос, но похоже, что вы говорите, что получаете обратный вызов об ошибке, и вы просто спрашиваете, как лучше всего не показывать ошибку? Почему бы вам просто не удалить веб-представление с экрана и / или не показать другое представление поверх него?


2

Здесь я нашел самое простое решение. проверить это ..

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

А вот и метод 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

Я полагаю, что если вы настаиваете на этом, вы можете просто проверить, есть ли ресурс, прежде чем вызывать функцию loadURL. Просто переопределите функции и выполните проверку перед вызовом super ()

ЗАМЕЧАНИЕ (возможно, не по теме): в http есть метод HEAD, который описывается следующим образом:

Метод HEAD идентичен GET, за исключением того, что сервер НЕ ДОЛЖЕН возвращать тело сообщения в ответе.

Этот метод может оказаться полезным. В любом случае, как бы вы это ни реализовали ... проверьте этот код:

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

Вы можете попробовать эту проверку, используя

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

6
Проверка наличия ресурса до его фактического перехода к нему удвоит нагрузку на сервер, поскольку на 1 виртуальный запрос будет приходиться 2 физических запроса. Это кажется излишним.
JoJo

ну отчаянные меры!
Sherif elKhatib

1
В любом случае вы все равно можете перегрузить его и получить HTML-контент, проверьте, есть ли в нем ошибка. Если не проанализировать и не отобразить
Sherif elKhatib

как это реализовать?
JoJo

@JoJo, на самом деле этот метод снижает нагрузку на сервер в случае недопустимого URL-адреса, так как возврат ответа HEAD имеет гораздо меньшие накладные расходы по сравнению с возвратом ответа 304 или 500.
Justin Shield

0

Я бы просто изменил веб-страницу на то, что вы используете для обработки ошибок:

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

все это можно найти на http://developer.android.com/reference/android/webkit/WebView.html


0

Сегодня я работал над проблемой избавления от этих раздражающих страниц ошибок Google. Это возможно с помощью примера кода Android, показанного выше и на множестве других форумов (спросите, откуда я знаю):

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

ЕСЛИ вы поместите его в shouldOverrideUrlLoading () как еще один веб-клиент. По крайней мере, у меня это работает на моем устройстве 2.3.6. Посмотрим, где еще это работает, позже. Я уверен, что сейчас это только расстроит меня. Немного о GoBack принадлежит мне. Вы можете этого не хотеть.


0

Попробуй это

 @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

Мы можем установить видимость webView на 0 (view.INVISIBLE) и показать какое-то сообщение. Этот код работает для моего приложения, работающего на 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

Мне приходилось сталкиваться с этой проблемой, и я также пытался решить ее с разных точек зрения. Наконец, я нашел решение, используя один флаг, чтобы проверить, произошла ли ошибка.

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

Таким образом, я скрываю страницу каждый раз, когда возникает ошибка, и показываю ее, когда страница загружается снова правильно.

Также добавлена ​​небольшая задержка на случай.

Я избегал решения с загрузкой пустой страницы, поскольку это не позволяет вам выполнять webview.reload () позже из-за добавления этой новой страницы в историю навигации.


-1

попробуйте это shouldOverrideUrlLoading , прежде чем перенаправить на другой URL-адрес, проверьте подключение к Интернету на основе этой страницы, должна быть загружена или нет.


-1

переопределить ваш метод WebViewClient

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

view.loadUrl('about:blank') имеет побочные эффекты, поскольку отменяет загрузку исходного URL-адреса.


view.clearView () устарел, см. stackoverflow.com/questions/3715482/clear-webview-content
silva96
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.