最近使用Android
中的WebView
开发应用,结果在实际使用的时候,前端开发的网页展现的时候,总是什么都看不到,结果跟踪分析发现,页面在加载完成后,就去获取页面的宽高,然后根据页面的宽高进行页面的渲染,但是页面加载完成后JavaScript
中得到的页面的宽高都是0,结果就导致整个的页面被渲染成了空页面。
项目是使用一个Dialog
里面嵌入WebView
的方式进行处理的,当网页加载完成之前,是不显示出来的,只有当网页加载完成后,页面才会展现出来。
实际的情况是,如果先展示Dialog
,等整个Dialog
已经完全展现出来之后,再去加载页面,这个时候,JavaScript
代码是可以获取到正确的宽高的。但是,如果先加载页面,然后等待页面的加载完成通知,再去显示页面的时候,这个时候,由于Dialog
还没有显示出来,此时内嵌的WebView
并没有得到实际的宽度高度,因此只能获得到0。
示例工程的代码如下:
布局文件layout_webview.xml
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layerType="software" /> </FrameLayout> |
对话框代码WebViewDialog.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
import android.app.Dialog; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Window; import android.view.WindowManager; import android.webkit.JavascriptInterface; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; public class WebViewDialog extends Dialog { public WebViewDialog(Context context) { super(context); setContentView(R.layout.layout_webview); Window window = getWindow(); WindowManager.LayoutParams params = window.getAttributes(); params.type = WindowManager.LayoutParams.TYPE_TOAST; params.height = WindowManager.LayoutParams.MATCH_PARENT; params.width = WindowManager.LayoutParams.MATCH_PARENT; window.setAttributes(params); mWebView = (WebView) findViewById(R.id.web_view); mWebView.setWebViewClient(mWebViewClientBase); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.addJavascriptInterface(new JsBridge(),"JsBridge"); } public void showWebView(){ final String strHtml = "<html>" + " <head>" + " <script>" + " window.JsBridge.jsGetWindowWithHeight(document.documentElement.clientWidth,document.documentElement.clientHeight);" + " </script>" + " </head>" + "</html>"; mWebView.loadData(strHtml,"text/html","UTF-8"); } public class JsBridge{ @JavascriptInterface public final void jsGetWindowWithHeight(int aWidth,int aHeight) { Log.d("jsGetWindowWithHeight","Width=" + aWidth + " Height=" + aHeight); Toast.makeText(getContext(),"Width=" + aWidth + " Height=" + aHeight,Toast.LENGTH_LONG).show(); } } private class WebViewClientBase extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { mHandler.removeCallbacks(mShowRunnable); mHandler.post(mShowRunnable); } } private final Runnable mShowRunnable = new Runnable() { @Override public void run() { WebViewDialog.this.show(); } }; private final WebViewClientBase mWebViewClientBase = new WebViewClientBase(); private WebView mWebView; private static Handler mHandler = new Handler(Looper.getMainLooper()); } |
主界面代码MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); webViewDialog = new WebViewDialog(this.getApplicationContext()); setContentView(R.layout.activity_main); Button btn = (Button)findViewById(R.id.btnClick); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { webViewDialog.showWebView(); } }); } private WebViewDialog webViewDialog; } |
主界面布局文件activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btnClick" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click" /> </RelativeLayout> |
完整运行整个例子,会发现最后输出的屏幕的宽高信息都是0。
这个问题发生的原因是由于界面在页面加载之后才会显示,由于页面还没有显示,因此从JavaScript
中获取页面的大小的时候,只能获取到0.
这个问题如果要求页面解决的话,那么编写网页的开发人员,只要监听window.onsize
事件即可。
如果要求WebView
的开发者来解决的话,则其解决方式如下:
1.修改WebViewDialog
初始化网页时候的策略,初始化时候禁止JavaScript
执行。WebViewDialog
构造函数中的mWebView.getSettings().setJavaScriptEnabled(true);
调整成mWebView.getSettings().setJavaScriptEnabled(false);
2.页面加载逻辑照旧执行。
3.实现Dialog
的onAttachedToWindow
函数,在这个函数中重新加载数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Override public void onAttachedToWindow() { /*此处我们呼应下面代码中禁用JavaScript的支持的部分代码 * 原因也已经解释的非常详细了 * 但是此处需要注意,就是先reload再次启用JavaScript这个顺序不要乱掉,否则 * 可能还没有调用reload之前,前一个页面已经执行了JavaScript导致页面上面的埋点两次执行。 * * 关于性能的隐忧,由于我们重新reload了页面,地址链接并没有改变,因此并不会去服务器上面重新获取页面 * 此处的性能隐忧,应该是不存在的 * * 至于是不是需要手工设置一下Chrome内核的缓存时间,这个在目前的实际实验观察看来,是不需要的。 * * */ mWebView.reload(); mWebView.getSettings().setJavaScriptEnabled(true); } |
最终的WebViewDialog.Java
的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
import android.app.Dialog; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Window; import android.view.WindowManager; import android.webkit.JavascriptInterface; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; public class WebViewDialog extends Dialog { public WebViewDialog(Context context) { super(context); setContentView(R.layout.layout_webview); Window window = getWindow(); WindowManager.LayoutParams params = window.getAttributes(); params.type = WindowManager.LayoutParams.TYPE_TOAST; params.height = WindowManager.LayoutParams.MATCH_PARENT; params.width = WindowManager.LayoutParams.MATCH_PARENT; window.setAttributes(params); mWebView = (WebView) findViewById(R.id.web_view); mWebView.setWebViewClient(mWebViewClientBase); /*此处禁用JavaScript,而是在后面onAttachedToWindow事件中再启用JavaScript的支持,同时强制页面刷新 * 这么处理的原因是由于前端同学在开发页面时候,会使用一个自有的JavaScript框架,这个框架会在页面 * 加载完成后,立即获取页面的宽高,但是此时获取到的宽高都是 0 ,因为此时Chrome还没有完成整个页面的Layout * 因此我们需要在页面完成Layout后再次加载页面才可以,但是如果此处启用JavaScript的支持会导致埋点数据的意外 * 上行,导致双份的埋点问题,因此,此处强制禁用JavaScript * */ mWebView.getSettings().setJavaScriptEnabled(false); mWebView.addJavascriptInterface(new JsBridge(),"JsBridge"); } @Override public void onAttachedToWindow() { /*此处我们呼应下面代码中禁用JavaScript的支持的部分代码 * 原因也已经解释的非常详细了 * 但是此处需要注意,就是先reload再次启用JavaScript这个顺序不要乱掉,否则 * 可能还没有调用reload之前,前一个页面已经执行了JavaScript导致页面上面的埋点两次执行。 * * 关于性能的隐忧,由于我们重新reload了页面,地址链接并没有改变,因此并不会去服务器上面重新获取页面 * 此处的性能隐忧,应该是不存在的 * * 至于是不是需要手工设置一下Chrome内核的缓存时间,这个在目前的实际实验观察看来,是不需要的。 * * */ mWebView.reload(); mWebView.getSettings().setJavaScriptEnabled(true); } public void showWebView(){ final String strHtml = "<html>" + " <head>" + " <script>" + " window.JsBridge.jsGetWindowWithHeight(document.documentElement.clientWidth,document.documentElement.clientHeight);" + " </script>" + " </head>" + "</html>"; mWebView.loadData(strHtml,"text/html","UTF-8"); } public class JsBridge{ @JavascriptInterface public final void jsGetWindowWithHeight(int aWidth,int aHeight) { Log.d("jsGetWindowWithHeight","Width=" + aWidth + " Height=" + aHeight); Toast.makeText(getContext(),"Width=" + aWidth + " Height=" + aHeight,Toast.LENGTH_LONG).show(); } } private class WebViewClientBase extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { mHandler.removeCallbacks(mShowRunnable); mHandler.post(mShowRunnable); } } private final Runnable mShowRunnable = new Runnable() { @Override public void run() { WebViewDialog.this.show(); } }; private final WebViewClientBase mWebViewClientBase = new WebViewClientBase(); private WebView mWebView; private static Handler mHandler = new Handler(Looper.getMainLooper()); } |