Mencegah WebView menampilkan "halaman web tidak tersedia"

88

Saya memiliki aplikasi yang memanfaatkan WebView secara ekstensif. Jika pengguna aplikasi ini tidak memiliki koneksi internet, halaman yang mengatakan "halaman web tidak tersedia" dan berbagai teks lainnya muncul. Adakah cara untuk tidak menampilkan teks umum ini di WebView saya? Saya ingin memberikan penanganan kesalahan saya sendiri.

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

Saya menemukan WebView-> clearView tidak benar-benar menghapus tampilan.

JoJo
sumber
3
Mengapa Anda tidak memeriksa koneksi internet sebelum menampilkan webView dan jika tidak ada fasilitas internet yang tersedia, Anda dapat melewati tampilan WebView dan sebaliknya Anda dapat menampilkan peringatan atau bersulang tanpa pesan internet?
Andro Selva
@JoJo dapatkah Anda mencentang jawaban sebagai benar? mungkin milik saya: P
Sherif elKhatib

Jawaban:

102

Pertama buat halaman kesalahan Anda sendiri di HTML dan taruh di folder aset Anda, sebut saja myerrorpage.html Kemudian dengan onReceivedError:

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

    }
});
SnowboardBruin
sumber
27
Halaman kesalahan lama "Halaman web tidak dimuat" muncul sepersekian detik sebelum digantikan oleh halaman kesalahan khusus dari aset
Cheenu Madan
1
Setuju dengan Cheenu. Harus ada solusi yang lebih baik.
Jozua
3
1. Anda dapat memuat konten web saat WebView tidak terlihat (dan mungkin menampilkan kemajuan yang buruk di atasnya) dan menampilkannya jika halaman berhasil dimuat / memuat "myerrorpage.html" karena kesalahan. 2. Anda harus menyadari bahwa onReceiveError akan dipicu jika ada masalah saat memuat masalah OR di JS yang berjalan setelah halaman dimuat (seperti fungsi jQuery yang berjalan di acara document.ready ()). Browser desktop - akan mengizinkan kesalahan JS tanpa memberi tahu pengguna tentang hal itu, seperti yang seharusnya dilakukan browser seluler.
FunkSoulBrother
5
tambahkan view.loadUrl("about:blank");sebelum loadUrl untuk mencegahnya menampilkan halaman kesalahan selama sepersekian detik.
Aashish
2
Di mana harus meletakkan file myerrorpage.html itu? Di mana letak folder aset android itu?
Nived Kannada
34

Solusi terbaik yang saya temukan adalah memuat halaman kosong di acara OnReceivedError seperti ini:

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

    view.loadUrl("about:blank");
}
7heViking
sumber
Ide bagus! Dalam kasus saya, saya menambahkan view.loadUrl(noConnectionUrl);dua kali (di mana noConnectionUrl adalah file lokal dengan pesan kesalahan). Itu juga membantu untuk tidak "melihat" laman kesalahan bawaan selama sepersekian detik.
hQuse
4
Harap dicatat bahwa, jika Anda menangani webView.goBack()maka pastikan untuk menangani kondisi halaman kosong ini. Jika tidak, halaman Anda sebelumnya akan dimuat dan jika gagal lagi maka Anda akan melihat halaman kosong ini lagi.
Chintan Shah
Lantas, Bagaimana cara terbaik untuk mengatasinya?
MoSoli
@MoSoli Itu semua tergantung pada kebutuhan Anda. Alternatif untuk menampilkan halaman kosong adalah dengan menampilkan beberapa UI kustom atau halaman web yang di-cache.
7heViking
10

Akhirnya, saya menyelesaikan ini. (Ini berfungsi sampai sekarang ..)

Solusi saya seperti ini ...

  1. Siapkan tata letak untuk menunjukkan kapan kesalahan terjadi alih-alih Halaman Web ('halaman tidak ditemukan pesan' kotor) Tata letak memiliki satu tombol, "RELOAD" dengan beberapa pesan panduan.

  2. Jika terjadi error, Remember using boolean dan tampilkan layout yang kita siapkan.

  3. Jika pengguna mengklik tombol "RELOAD", setel mbErrorOccured ke false. Dan Set mbReloadPressed ke true.
  4. jika mbErrorOccured salah dan mbReloadPressed benar, itu berarti webview berhasil memuat halaman. Karena jika terjadi kesalahan lagi, mbErrorOccured akan disetel menjadi true pada onReceivedError (...)

Ini sumber lengkap saya. Lihat ini.

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

Tambahan: Berikut layoutnya ....

<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.dll
sumber
Bisakah Anda membagikan file sumber daya Anda? Saya pemula
Rajan Rawal
@RajanRawal Saya mengedit dan menambahkan contoh tata letak. :)
cmcromance
Saya tidak dapat melihat bilah kemajuan, tetapi saya dapat melihat kode di sana. :-(
Rajan Rawal
8

Jika tampilan web disematkan dalam beberapa tampilan ubahsuaian sehingga pengguna hampir percaya bahwa dia melihat tampilan asli dan bukan tampilan web, dalam skenario seperti itu menunjukkan kesalahan "laman tidak dapat dimuat" adalah hal yang tidak masuk akal. Apa yang biasanya saya lakukan dalam situasi seperti itu adalah saya memuat halaman kosong dan menampilkan pesan bersulang seperti di bawah ini

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

Lihat diskusi di Android WebView onReceivedError () . Ini cukup panjang, tetapi konsensusnya tampaknya adalah a) Anda tidak dapat menghentikan kemunculan halaman "halaman web tidak tersedia", tetapi b) Anda selalu dapat memuat halaman kosong setelah mendapatkan onReceivedError

Torid
sumber
Orang-orang ini berbicara tentang onReceivedErrortidak memicu sama sekali. Ini memicu dengan benar dalam kode saya ketika tidak ada koneksi internet.
JoJo
2

Anda dapat menggunakan GETpermintaan untuk mendapatkan konten halaman dan kemudian menampilkan data tersebut menggunakan Webview, dengan cara ini Anda tidak menggunakan beberapa panggilan server. Alternatif yang bisa Anda gunakan Javascriptuntuk memeriksa DOMvaliditas objek.

Ravi Vyas
sumber
itu ide yang bagus, namun, sangat disayangkan kita perlu melakukan sesuatu seperti ini ...
Jeffrey Blattman
2

Mungkin saya salah paham dengan pertanyaannya, tetapi sepertinya Anda mengatakan bahwa Anda menerima panggilan balik yang salah, dan Anda hanya bertanya apa cara terbaik untuk tidak menampilkan kesalahan? Mengapa Anda tidak menghapus tampilan web dari layar dan / atau menampilkan tampilan lain di atasnya?

Matthew Horst
sumber
2

Di sini saya menemukan solusi termudah. Lihat ini..

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

Dan inilah metode 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
sumber
1

Saya kira jika Anda bersikeras melakukan ini, Anda bisa memeriksa apakah sumber daya ada sebelum memanggil fungsi loadURL. Cukup timpa fungsinya dan lakukan pemeriksaan sebelum memanggil super ()

KETERANGAN (mungkin di luar topik): Di http, ada metode yang disebut HEAD yang dijelaskan sebagai berikut:

Metode HEAD identik dengan GET, kecuali bahwa server TIDAK HARUS mengembalikan badan pesan dalam respons

Metode ini mungkin berguna. Pokoknya bagaimana pun Anda menerapkannya ... periksa kode ini:

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

Anda dapat mencoba pemeriksaan ini menggunakan

if(url.openConnection().getContentLength() > 0)
Sherif elKhatib
sumber
6
Memeriksa apakah sumber daya ada di sana sebelum benar-benar pergi ke sana akan menggandakan beban di server karena akan ada 2 permintaan fisik per 1 permintaan virtual. Ini sepertinya sedikit berlebihan.
JoJo
tindakan putus asa baik!
Sherif elKhatib
1
Bagaimanapun Anda masih bisa membebani dan mendapatkan konten html, periksa apakah ada kesalahan. Jika tidak mengurai dan menampilkannya
Sherif elKhatib
bagaimana sebenarnya menerapkan ini?
JoJo
@JoJo, sebenarnya metode ini mengurangi beban di server jika terjadi url yang tidak valid karena mengembalikan respons HEAD memiliki overhead yang jauh lebih kecil vs mengembalikan respons 304 atau 500.
Justin Shield
0

Saya hanya akan mengubah halaman web menjadi apa pun yang Anda gunakan untuk penanganan kesalahan:

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

ini semua dapat ditemukan di http://developer.android.com/reference/android/webkit/WebView.html

Efraim
sumber
0

Saya telah mengatasi masalah membuang halaman kesalahan Google yang menjengkelkan hari ini. Ini dimungkinkan dengan kode contoh Android yang terlihat di atas dan di banyak forum lain (tanyakan bagaimana saya tahu):

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

JIKA Anda memasukkannya ke shouldOverrideUrlLoading () sebagai satu klien web lagi. Setidaknya, ini berfungsi untuk saya di perangkat 2.3.6 saya. Kami akan melihat di mana lagi ini berfungsi nanti. Itu hanya akan membuatku depresi sekarang, aku yakin. Bit goBack adalah milikku. Anda mungkin tidak menginginkannya.

R Earle Harris
sumber
0

Coba ini

 @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());
      }
 }
Virat 18
sumber
0

Kita dapat mengatur visibilitas webView menjadi 0 (view.INVISIBLE) dan menampilkan beberapa pesan. Kode ini berfungsi untuk aplikasi saya yang berjalan di 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
sumber
0

Saya harus menghadapi masalah ini dan juga mencoba menyelesaikannya dari perspektif yang berbeda. Akhirnya saya menemukan solusi dengan menggunakan satu flag untuk mengecek apakah terjadi 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());
    }
 }

Dengan cara ini saya menyembunyikan halaman setiap kali ada kesalahan dan menampilkannya ketika halaman telah dimuat lagi dengan benar.

Juga menambahkan penundaan kecil dalam kasus.

Saya menghindari solusi memuat halaman kosong karena tidak memungkinkan Anda melakukan webview.reload () nanti karena menambahkan halaman baru itu dalam riwayat navigasi.

Jose M Lechon
sumber
-1

coba ini shouldOverrideUrlLoading , sebelum redirect ke url lain periksa koneksi internet berdasarkan halaman itu harus dimuat atau tidak.

Bipin Vayalu
sumber
-1

ganti metode WebViewClient Anda

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

view.loadUrl('about:blank') memiliki efek samping, karena membatalkan pemuatan URL asli.

duckduckgo.dll
sumber
view.clearView () tidak digunakan lagi, lihat stackoverflow.com/questions/3715482/clear-webview-content
silva96