Android WebView with https loadUrl shows blank/empty page

本文介绍了解决Android WebView加载HTTPS页面显示为空白的问题。针对Android 1.6及更高版本,在没有公共API允许手动处理不受信任网站的情况下,提供了一个使用私有API的解决方案,该方案同样适用于Froyo及更新版本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android WebView with https loadUrl shows blank/empty page

September 28, 2010

I recently encountered this problem while trying to develop a WebView that is supported on Doughnut (1.6) and above. When presented with a https url the WebView just renders a blank page.

It seems that until Froyo, Android didn’t provide a public API to let you manually decide if you wanted to proceed to an untrusted web site via a WebView.

Note that in this case it is not even that the web site was untrusted (in the conventional sense) – it is because Thawte is not in the default list of trusted certificate authorities on Android. If you use the standard web browser on Android, the browser presents a typical warning dialog (as presented below) that enables you to accept the certificate and carry on.

Invalid certificate warning

If you are using Froyo as the target SDK then you can use:

1 engine = (WebView) findViewById(R.id.my_webview);
2 engine.setWebViewClient(new WebViewClient() {
3  public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
4  handler.proceed() ;
5  }
6 }

However, if you are not using Froyo then it seems you are out of luck. After some research, I concluded that I had the following options:

a) intercept all page requests and forward to external web browser if url begins with https (not a clean or nice user experience and totally unnecessary for Froyo and above)
b) add certificate of website to local keystore (in this case I am serving multiple web pages and the origin of many of these is not known until runtime)
c) make Froyo the minSDK and discard previous versions of Android (not a suitable option)
d) hack and use some private apis (the option described below)

To solve this problem we have to use a private interface (not published on SDK but present in real SDK runtime). As you can see in the Android src tree, theonReceivedSslError is indeed present (and used – it simply cancels the request) in Doughnut. However, this method is not presented to us in the SDK  - it is hidden because it contains a parameter type SslError which is located in a hidden package.  Therefore, we need to copy these src files into our project so that we can access them:

1) Copy WebViewClient.java into the package “android.webkit” within your src folder.
2) Copy SslError.java into the package “android.net.http” within your src folder.

Src files

3) Since we replicated the paths to the src files in the SDK, our code to override onSslError above now works on Android 1.6.

Caution: bear in mind that we are using a private API and Google reserve the right to change private APIs at any time – though in this case it is unlikely since they’ve now made this available in Froyo.

20 Responses to “Android WebView with https loadUrl shows blank/empty page”

  1. Bano Says:

    Hi, I have used this method and other method (like thishttp://wongcr.blogspot.com/2009/03/disabling-certificate-validation-in.html) and I can’t see a https page in the WebView. Can you help me?


  2. I can try. Your request for help is a bit vague though…

    • Bano Says:

      Sorry, I did’t know if I should write a large post. My problem is with the HttpPost connections, I create an “DefaultHttpClient” and a “HttpPost”, next I create a list of values to send and finally execute it.

      This work in http conections, but in https conections I try the methods I find on Internet and don’t get it to work (Get not trusted certificate).

      The last code (with custom SSLShocketFactory and TrustManager):
      try {
      // //
      SchemeRegistry schemeRegistry = new SchemeRegistry();

      schemeRegistry.register(new Scheme(“https”,new EasySSLSocketFactory(), 443));

      HttpParams params = new BasicHttpParams();
      SingleClientConnManager mgr = new SingleClientConnManager(params, schemeRegistry);

      DefaultHttpClient hc = new DefaultHttpClient(mgr, params);
      // /

      // DefaultHttpClient hc = new DefaultHttpClient();
      ResponseHandler res = new BasicResponseHandler();
      HttpPost postMethod = new HttpPost(DIRECCION);
      List nameValuePairs = new ArrayList(3);
      nameValuePairs.add(new BasicNameValuePair(“usuario”, nom));
      nameValuePairs.add(new BasicNameValuePair(“tipo_firma”, tip));
      nameValuePairs.add(new BasicNameValuePair(“string_img”, img));
      postMethod.setEntity(new UrlEncodedFormEntity(nameValuePairs));
      String response = hc.execute(postMethod, res);
      return response;
      } catch (ClientProtocolException e) {
      pasos = “Excepción: ” + e.toString();
      } catch (IOException e) {
      // “Error 1: Unable to establish communication with the server. Check your configuration.”
      pasos = pasos + “Excepción: ” + e.toString();
      }

      if you can help me, it would very helpfull.

      PD: Sorry for my bad english…


  3. The best way would be to add the server’s public certificate to your Android App’s trusted store:

    http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html

    Otherwise you could simply stop checking all hosts for their certificate:

    http://stackoverflow.com/questions/995514/https-connection-android/1000205#1000205

    • Bano Says:

      Finally, your post give me an idea. I change the DefaultHttpClient for HttpsURLConection, using OutputStream and InputStream to the comunication. Next I apply the second link you give me (I visited it yet but didn’t help me in my previously idea).

      I can’t use the SSL certificate creator because I haven’t direct access to the server of the proyect.

      the code of Send:
      try {
      // //
      String location = “usuario=” + nom + “&tipo_firma=” + tip + “&string_img=” + img;
      URL url = new URL(DIRECCION);
      trustAllHosts();
      HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
      https.setHostnameVerifier(DO_NOT_VERIFY);
      https.setRequestMethod(“POST”);
      https.setDoOutput(true);
      https.setUseCaches(false);
      https.setRequestProperty(“Content-Type”,
      “application/x-www-form-urlencoded”);
      https.connect();
      OutputStream ost = https.getOutputStream();
      DataOutputStream out = new DataOutputStream(ost);
      out.writeBytes(location);
      out.flush();
      ost = null;

      InputStream ist = https.getInputStream();
      BufferedReader in = new BufferedReader(new InputStreamReader(ist));
      StringBuffer sb = new StringBuffer();
      char[] c = new char[1];
      while (in.read(c, 0, 1) == 1) {
      sb.append(c[0]);

      }
      String response = sb.toString();
      // /
      return response;
      } catch (ClientProtocolException e) {
      pasos = “Excepción: ” + e.toString();
      } catch (IOException e) {

      // “Error 1: Unable to establish communication with the server. Check your configuration.”
      pasos = pasos + “Excepción: ” + e.toString();
      }

      Next adding the trust part:
      // always verify the host – dont check for certificate
      final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
      public boolean verify(String hostname, SSLSession session) {
      return true;
      }
      };
      /**
      * Trust every server – dont check for any certificate
      */
      private static void trustAllHosts() {
      // Create a trust manager that does not validate certificate chains
      TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
      return new java.security.cert.X509Certificate[] {};
      }

      public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {}
      public void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException {}
      } };

      // Install the all-trusting trust manager
      try {
      SSLContext sc = SSLContext.getInstance(“TLS”);
      sc.init(null, trustAllCerts, new java.security.SecureRandom());
      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
      } catch (Exception e) {
      e.printStackTrace();
      }
      }

      Thanks for your help.

    • Vito Andolini Says:

      Crazy Bob’s article is a solution for SSL issues with the HttpClient, not with a WebView. I don’t believe you can add a certificate to the app’s trusted store and have that used by a WebView.

  4. William Says:

    Hi,

    Just wanted to thank you to have resolved my problem in 2 seconds.

    Working on eclair 2.1 at the moment I had exactly the same issue you described. Your fixed avoided me a lot of troubles.

    thanks

    William

  5. Ansari Says:

    Thanks a lot, the above solution works.

  6. Sebastian B Says:

    Thank you very much, this is excellent!!!
    I had been struggling with this for some hours before I found your solution.


  7. Thanks, was having the same problem in a project targeting Android 1.6. Now I know what the heck is going on.


  8. [...] WebView just swallowed this with no error messages, but a bit of googling and found the following blog post about how to work round the certificate miss match and even how to make it work with Android [...]

  9. skshelkehelke Says:

    Hi All,
    I am trying to retrieve POST parameter from Response. but not able get it from android Webview.

    Could anyone help me or give an idea to retrieve POST data from response URL.

    Thanks & Regards

  10. Richard Says:

    I have applied your trick and it works fine until I run on a real Android device 1.6 or 2.1. Then the overloading of classes is stop by the DEX loader !!! any tricks to get around ?

    • Richard Says:

      08-15 12:03:41.938: DEBUG/dalvikvm(3003): DexOpt: ‘Landroid/net/http/SslError;’ has an earlier definition; blocking out
      08-15 12:03:41.938: DEBUG/dalvikvm(3003): DexOpt: ‘Landroid/webkit/WebViewClient;’ has an earlier definition; blocking out
      08-15 12:03:41.973: DEBUG/dalvikvm(3003): DexOpt: not verifying ‘Landroid/net/http/SslError;’: multiple definitions
      08-15 12:03:41.973: DEBUG/dalvikvm(3003): DexOpt: not verifying ‘Landroid/webkit/WebViewClient;’: multiple definitions
      08-15 12:03:42.153: INFO/dalvikvm(3003): DexOpt: not resolving ambiguous class ‘Landroid/webkit/WebViewClient;’

  11. tomozeron Says:

    Hi Damian,

    I’ve been struggling a couple of nights getting postUrl to work when posting to a https site. I tried to use all sorts of TrustedManagers etc etc but with no luck.
    Then I found this gem of yours, read it and in less than 10 minutes I was up’n running.

    I bow my head in humble submission.

    Thank you.

    /brian

  12. rpche Says:

    Hello, Damian thnx for the tutorial but i still dont manage to get it work. mayb if u have some time u can help me load https in my webview :)

    package here.testing.a.cool.app;

    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Window;
    import android.webkit.WebChromeClient;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;

    public class WebPageLoader extends Activity
    {
    final Activity activity = this;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    this.getWindow().requestFeature(Window.FEATURE_PROGRESS);
    setContentView(R.layout.main);
    WebView webView = (WebView) findViewById(R.id.webview);
    webView.getSettings().setJavaScriptEnabled(true);

    webView.setWebChromeClient(new WebChromeClient() {
    public void onProgressChanged(WebView view, int progress)
    {
    activity.setTitle(“Grabbing the bits and the bytes..”);
    activity.setProgress(progress * 100);

    if(progress == 100)
    activity.setTitle(R.string.app_name);
    }
    });

    webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
    {
    // Handle the error
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url)
    {
    view.loadUrl(url);
    return true;
    }
    });

    webView.loadUrl(“https://website.willnot.open”);
    }
    }

  13. Arthur Says:

    Thanks a lot!!!!

  14. becomingthebeast Says:

    Great post Damian.
    As always your articles are most useful.
    Thank you!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值