Запретить 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 на самом деле не очищает представление.

JoJo
источник
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");
}
7heViking
источник
Хорошая идея! В моем случае я добавил 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>

cmcromance
источник
Не могли бы вы поделиться своим файлом ресурсов? Я новичок.
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 принадлежит мне. Вы можете этого не хотеть.

R Эрл Харрис
источник
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());
      }
 }
Вират18
источник
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