很多APP都嵌套了webview,腾讯的webview的简单使用。
腾讯webview的sdk下载地址
用法:
1、布局文件:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<com.xinggui.wz.chuangjiaoplatform.view.MyCustomViewTitleLayout
android:id="@+id/custom1_webview"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:progressDrawable="@drawable/progressbar"
android:visibility="gone"/>
<com.tencent.smtt.sdk.WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none" />
</LinearLayout>
2、再就是onCreate里面的代码:
myWebView=(WebView) findViewById(R.id.webview);
progressBar=(ProgressBar) findViewById(R.id.progressBar1);
initWebView();//初始化webview的设置,注释写的很清楚
String url="你的URL"
myWebView.loadUrl(url);
private void initWebView() {
WebSettings webSettings=myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);//支持js
// myWebView.requestFocusFromTouch();//如果webView中需要用户手动输入用户名、密码或其他,则webview必须设置支持获取手势焦点
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //允许js弹窗
myWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String s) {
webView.loadUrl(s);
return true;
}
});
myWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView webView, String s, String s1, JsResult jsResult) {
new AlertDialog.Builder(WebViewActivity.this).setTitle("提示消息").setMessage(s1).setPositiveButton("OK", null).show();
jsResult.confirm(); //不调用,alert只弹出一次
return true;
}
@Override
public void onProgressChanged(WebView webView, int newProgress) {
super.onProgressChanged(webView, newProgress);
if (newProgress==100) {
progressBar.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(newProgress);//设置加载进度
}
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//如果不做任何处理,浏览网页,点击系统“Back”键,整个Browser会调用finish()而结束自身,
// 如果希望浏览的网 页回退而不是推出浏览器,需要在当前Activity中处理并消费掉该Back事件。
if (keyCode==KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
项目就不贴出来了,代码很齐全,直接新建项目拷贝就能用
什么要学习Android与H5互调?
微信,QQ空间等大量软件都内嵌了H5,不得不说是一种趋势。Android与H5互调可以让我们的实现混合开发,至于混合开发就是在一个App中内嵌一个轻量级的浏览器,一部分原生的功能改为Html 5来开发。
优势:使用H5实现的功能能够在不升级App的情况下动态更新,而且可以在Android或iOS的App上同时运行,节约了成本,提高了开发效率。
原理:其实就是Java代码和JavaScript之间的调用。
开局插入一张文章的目录结构:
WebView简介
要实现Android与H5互调,WebView是一个很重要的控件,WebView可以很好地帮助我们展示html页面,所以有必要先了解一下WebView。
一丶WebView常用方法
//加载assets目录下的test.html文件 webView.loadUrl("file:///android_asset/test.html"); //加载网络资源(注意要加上网络权限) webView.loadUrl("http://blog.csdn.net");
webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if("http://www.jikedaohang.com/".equals(url)) { view.loadUrl("https://www.baidu.com/"); } return true; } });
mWebView.setWebViewClient(new WebViewClient(){ @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { WebResourceResponse response=null; if (url.contains("logo")) { try { InputStream logo=getAssets().open("logo.png"); response=new WebResourceResponse("image/png", "UTF-8", logo); } catch (IOException e) { e.printStackTrace(); } } return response; } });
webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); // 开始加载网页时处理 如:显示"加载提示" 的加载对话框 ... } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 网页加载完成时处理 如:让 加载对话框 消失 ... } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); // 加载网页失败时处理 如:提示失败,或显示新的界面 ... } });
webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); // 接受信任所有网站的证书 // handler.cancel(); // 默认操作 不处理 // handler.handleMessage(null); // 可做其他处理 } });
webView.setWebChromeClient(new WebChromeClient() { public void onProgressChanged(WebView view, int progress) { setTitle("页面加载中,请稍候..." + progress + "%"); setProgress(progress * 100); if (progress==100) { //... } } });
//1.首先在WebView初始化时添加如下代码 if(Build.VERSION.SDK_INT >=19) { /*对系统API在19以上的版本作了兼容。因为4.4以上系统在onPageFinished时再恢复图片加载时,如果存在多张图片引用的是相同的src时,会只有一个image标签得到加载,因而对于这样的系统我们就先直接加载。*/ webView.getSettings().setLoadsImagesAutomatically(true); } else { webView.getSettings().setLoadsImagesAutomatically(false); } //2.在WebView的WebViewClient子类中重写onPageFinished()方法添加如下代码: @Override public void onPageFinished(WebView view, String url) { if(!webView.getSettings().getLoadsImagesAutomatically()) { webView.getSettings().setLoadsImagesAutomatically(true); } }
class MyDownloadListenter implements DownloadListener{ @Override public void onDownloadStart(String url, String userAgent,String contentDisposition, String mimetype, long contentLength) { //下载任务...,主要有两种方式 //(1)自定义下载任务 //(2)调用系统的download的模块 Uri uri=Uri.parse(url); Intent intent=new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }
webview.setDownloadListener(new MyDownloadListenter());
public boolean onKeyDown(int keyCode, KeyEvent event) { //其中webView.canGoBack()在webView含有一个可后退的浏览记录时返回true if ((keyCode==KeyEvent.KEYCODE_BACK) && webView.canGoBack()) { webView.goBack(); return true; } return super.onKeyDown(keyCode, event); } }
二丶WebSettings配置
WebSettings webSettings=webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setDomStorageEnabled(true);
settings.setDatabasePath(cacheDirPath);
settings.setAppCachePath(cacheDirPath);
settings.setDefaultTextEncodingName(“utf-8”);
settings.setUseWideViewPort(false);
settings.setSupportZoom(true);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
settings.supportMultipleWindows();
settings.setAllowFileAccess(true);
settings.setNeedInitialFocus(true);
settings.setBuiltInZoomControls(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setLoadWithOverviewMode(true);
settings.setLoadsImagesAutomatically(true);
三丶WebViewClient 的回调方法列表
WebViewClient主要用来辅助WebView处理各种通知、请求等事件,通过setWebViewClient方法设置。
(1)更新历史记录
doUpdateVisitedHistory(WebView view, String url, boolean isReload)
(2)应用程序重新请求网页数据
onFormResubmission(WebView view, Message dontResend, Message resend)
(3)在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
onLoadResource(WebView view, String url)
(4)开始载入页面调用,通常我们可以在这设定一个loading的页面,告诉用户程序在等待网络响应。
onPageStarted(WebView view, String url, Bitmap favicon)
(5)在页面加载结束时调用。同样道理,我们知道一个页面载入完成,于是我们可以关闭loading 条,切换程序动作。
onPageFinished(WebView view, String url)
(6)报告错误信息
onReceivedError(WebView view, int errorCode, String description, String failingUrl)
(7)获取返回信息授权请求
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host,String realm)
(8)重写此方法可以让webview处理https请求。
onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
(9)WebView发生改变时调用
onScaleChanged(WebView view, float oldScale, float newScale)
(10)Key事件未被加载时调用
onUnhandledKeyEvent(WebView view, KeyEvent event)
(11)重写此方法才能够处理在浏览器中的按键事件。
shouldOverrideKeyEvent(WebView view, KeyEvent event)
(12)在网页跳转时调用,这个函数我们可以做很多操作,比如我们读取到某些特殊的URL,于是就可以不打开地址,取消这个操作,进行预先定义的其他操作,这对一个程序是非常必要的。
shouldOverrideUrlLoading(WebView view, String url)
(13)在加载某个网页的资源的时候多次调用(已过时)
shouldInterceptRequest(WebView view, String url)
(14)在加载某个网页的资源的时候多次调用
shouldInterceptRequest(WebView view, WebResourceRequest request)
注意:
shouldOverrideUrlLoading在网页跳转的时候调用,且一般每跳转一次只调用一次。
shouldInterceptRequest只要是网页加载的过程中均会调用,资源加载的时候都会回调该方法,会多次调用。
四丶WebChoromeClient的回调方法列表
WebChromeClient主要用来辅助WebView处理Javascript的对话框、网站图标、网站标题以及网页加载进度等。通过WebView的setWebChromeClient()方法设置。
(1)监听网页加载进度
onProgressChanged(WebView view, int newProgress)
(2)监听网页标题 : 比如百度页面的标题是“百度一下,你就知道”
onReceivedTitle(WebView view, String title)
(3)监听网页图标
onReceivedIcon(WebView view, Bitmap icon)
Java和JavaScript互调
为方便展示,使用addJavascriptInterface方式实现与本地js交互(存在漏洞)。也可通过其他方式实现,比如拦截ur进行参数解析l等。
Java调JS
function javaCallJs(arg){ document.getElementById("content").innerHTML=("欢迎:"+arg ); }
webView.loadUrl("javascript:javaCallJs("+"'"+name+"'"+")");
以上代码就是调用了JS中一个叫javaCallJs(arg)的方法,并传入了一个name参数。(具体效果下面有展示)
JS调java
webView.addJavascriptInterface(new JSInterface (),"Android");
class JSInterface { @JavascriptInterface public void showToast(String arg){ Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show(); } }
<input type="button" value="点击Android被调用" onclick="window.Android.showToast('JS中传来的参数')"/>
window.Android.showToast(‘JS中传来的参数’)”中的”Android”即addJavascriptInterface()中指定的,并且JS向java传递了参数,类型为String。而showToast(String arg)会以Toast的形式弹出此参数。
java与JS互调代码示例
先看效果图:
不好意思,传错了,是这张:
代码非常简单,并且加了注释,直接看代码就可以了。
<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <script type="text/javascript"> function javaCallJs(arg){ document.getElementById("content").innerHTML=("欢迎:"+arg ); } </script> </head> <body> <div id="content"> 请在上方输入您的用户名</div> <input type="button" value="点击Android被调用" onclick="window.Android.showToast('JS中传来的参数')"/> </body> </html>
javaCallJs是java调用JS的方法,showToast方法是JS调用java的方法
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/ll_root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:background="#000088"> <EditText android:id="@+id/et_user" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="输入WebView中要显示的用户名" android:background="#008800" android:textSize="16sp" android:layout_weight="1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="40dp" android:layout_marginRight="20dp" android:textSize="16sp" android:text="确定" android:onClick="click"/> </LinearLayout> </LinearLayout>
很简单,就是一个输入框和一个确定按钮,点击按钮会调用JS中的方法。
package com.wangjian.webviewdemo; import android.annotation.SuppressLint; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private WebView webView; private LinearLayout ll_root; private EditText et_user; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ll_root=(LinearLayout) findViewById(R.id.ll_root); et_user=(EditText) findViewById(R.id.et_user); initWebView(); } //初始化WebView private void initWebView() { //动态创建一个WebView对象并添加到LinearLayout中 webView=new WebView(getApplication()); LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); webView.setLayoutParams(params); ll_root.addView(webView); //不跳转到其他浏览器 webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }); WebSettings settings=webView.getSettings(); //支持JS settings.setJavaScriptEnabled(true); //加载本地html文件 webView.loadUrl("file:///android_asset/JavaAndJavaScriptCall.html"); webView.addJavascriptInterface(new JSInterface(),"Android"); } //按钮的点击事件 public void click(View view){ //java调用JS方法 webView.loadUrl("javascript:javaCallJs(" + "'" + et_user.getText().toString()+"'"+")"); } //在页面销毁的时候将webView移除 @Override protected void onDestroy() { super.onDestroy(); ll_root.removeView(webView); webView.stopLoading(); webView.removeAllViews(); webView.destroy(); webView=null; } private class JSInterface { //JS需要调用的方法 @JavascriptInterface public void showToast(String arg){ Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show(); } } }
需要注意的地方
参考链接:安卓webview的一些坑
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); wv.saveState(outState); Log.e(TAG, "save state..."); }
恢复状态(在activity的onCreate(bundle savedInstanceState)里)
if(null!=savedInstanceState){ wv.restoreState(savedInstanceState); Log.i(TAG, "restore state"); }else{ wv.loadUrl("http://3g.cn"); }
其他一些常见问题:
1. WebViewClient.onPageFinished()。
你永远无法确定当WebView调用这个方法的时候,网页内容是否真的加载完毕了。当前正在加载的网页产生跳转的时候这个方法可能会被多次调用,StackOverflow上有比较具体的解释(How to listen for a Webview finishing loading a URL in Android?), 但其中列举的解决方法并不完美。所以当你的WebView需要加载各种各样的网页并且需要在页面加载完成时采取一些操作的话,可能WebChromeClient.onProgressChanged()比WebViewClient.onPageFinished()都要靠谱一些。
2. WebView后台耗电问题。
当你的程序调用了WebView加载网页,WebView会自己开启一些线程(?),如果你没有正确地将WebView销毁的话,这些残余的线程(?)会一直在后台运行,由此导致你的应用程序耗电量居高不下。对此我采用的处理方式比较偷懒,简单又粗暴(不建议),即在Activity.onDestroy()中直接调用System.exit(0),使得应用程序完全被移出虚拟机,这样就不会有任何问题了。
3. 切换WebView闪屏问题。
如果你需要在同一个ViewGroup中来回切换不同的WebView(包含了不同的网页内容)的话,你就会发现闪屏是不可避免的。这应该是Android硬件加速的Bug,如果关闭硬件加速这种情况会好很多,但无法获得很好的浏览体验,你会感觉网页滑动的时候一卡一卡的,不跟手。
4. 在某些手机上,Webview有视频时,activity销毁后,视频资源没有被销毁,甚至还能听到在后台播放。即便是像刚才那样各种销毁webview也无济于事,解决办法:在onDestory之前修改url为空地址。
5.WebView硬件加速导致页面渲染闪烁问题
关于Android硬件加速 开始于Android 3.0 (API level 11),开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是容易会出现页面加载白块同时界面闪烁现象。解决这个问题的方法是设置WebView暂时关闭硬件加速 代码如下:
if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.HONEYCOMB) {
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
JavaScript简介
JavaScript是一种动态类型的脚本语言;在1995年时,由Netscape公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。因为Netscape与Sun合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。
JavaScript脚本语言具有以下特点:
(1) 脚本语言。JavaScript是一种解释型的脚本语言,是在程序的运行过程中逐行进行解释执行,不需要预编译。而Java、C++等语言需要先编译后执行;
(2) 动态性。JavaScript能够动态修改对象的属性,没有办法在编译的时候知道变量的类型,只有在运行的时候才能确定。而Java、C++等都是静态类型语言,他们在编译的时候就能够知道每个变量的类型;
(3) 跨平台性。JavaScript脚本语言不依赖于操作系统,仅需要浏览器的支持。可以在多种平台下运行(如Windows、Linux、Mac、Android、IOS等)。
二
JavaScript与Java语言区别
从上面介绍的JavaScript语言特点会发现JavaScript的效率会比Java、C++低很多;看以下这个实例:
当JavaScript引擎分析到该段代码时,根本不知道a和b是什么类型,唯一的办法就是运行的时候根据实际传过来的对象再来计算,这显然会导致严重的性能问题。
当编译上面Java代码的时候,根据右边类型Class1的定义,获取对象a的属性x的时候,其实就是对象a的地址,大小是一个整形。同时获取对象b的属性y的时候,其实就是对象b的地址加上4个字节,这些都是在生成本地代码时确定的,无需在运行本地代码的时候再决定他们的地址和类型是什么,这显然能够节省时间。
再看一下两者分别是怎样存储对象a和b的:
对于传统的JavaScript解释器来说,因为不知道a和b的具体类型,就用属性名-属性值对来保存,之后访问对象的属性值时就需要通过属性名匹配来获取对应的值;对象b也是同样的结果来保存相同的属性。随着对象的增多,这显然带来了巨大的空间浪费。
而上面的Java代码在编译时就确定了类Class1的成员类型,访问x就是对象a的地址,y就是a的地址加上4个字节;所以字符“x”和“y”运行时都不再需要,因为不再需要额外查找这些属性地址的工作。
从上面实例可以看到JavaScript和Java语言的区别包括以下几个部分:
编译确定位置:Java有编译和执行两个阶段,位置的偏移信息都是在编译器编译的时候决定的,当Java生成本地代码之后,对象的属性和偏移信息都计算完成。而JavaScript没有类型,只有在对象执行创建的时候才确定这些信息,而且JavaScript语言能够在执行时修改对象的属性。
偏移信息共享:Java有类型定义,所有的对象都是共享偏移信息的,访问他们只需要按照编译时确定的偏移量即可。JavaScript则不同,每个对象都有自我描述,属性和位置偏移信息都包含在自身的结构中。
偏移信息查找:Java查找偏移地址很简单,都是在编译代码时,对使用到的类型成员变量直接设置偏移量。而JavaScript则需要通过属性名匹配才能查找到对应的值。
Java语言有明显的两个阶段:编译和运行,如下图所示:
Java代码经过编译器编译之后生成的是字节码,字节码是跨平台的一种中间表示,不同于本地代码。该字节码与平台无关,能够在不同的操作系统上运行。在运行字节码阶段,Java的运行环境是Java虚拟机加载字节码。Java虚拟机一般都引入JIT技术来将字节码转变成本地代码来提高执行效率。第一阶段对时间要求不严格,第二阶段对每个步骤所花费的时间非常敏感,时间越短越好。
JavaScript语言的编译和执行都是在运行阶段执行的,如下图所示:
因为都是在代码运行过程中来处理这些步骤,所以每个阶段的时间越短越好,而且每引入一个阶段都是额外的时间开销,所以一个JavaScript引擎主要包含以下几个部分:
编译器:主要工作是将源代码编译成抽象语法树;
解释器:主要是接受字节码,解释执行这个字节码;
JIT工具:将字节码或抽象语法树转换成本地代码;
垃圾回收期和分析工具(Profiler):负责垃圾回收和收集引擎中的信息,帮助改善引擎的性能。
三
V8引擎介绍
V8是一个JavaScript引擎实现的开源项目,最开始由一些语言学家设计出来,后被Google收购,成为了JavaScript引擎和众多相关技术的引领者。V8支持众多的操作系统,包括Windows、Linux、Android、Mac OS X等。同时它也能够支持众多的硬件架构,如IA32、X64、ARM、MIPS等,将主流软硬件平台一网打尽。由于它是一个开源项目,开发者可以自由使用它的强大能力,目前炙手可热的NodeJs项目就是基于V8项目研发的。
第一条语句:表示建立一个域,用于包含一组Handle对象,便于管理和释放它们;
第二条语句:根据Isolate对象来获取一个Context对象,使用Handle来管理。Handle对象本身存放在栈上,而实际的Context对象保存在堆中;
第三条语句:根据两个对象Isolate和Context来创建一个函数间使用的对象,使用Persistent类来管理;
第四条语句:表示为Context对象创建一个基于栈的域,下面的执行步骤都是在该域中对应的上下文中来进行的;
第五条语句:读入一段JavaScript代码;
第六条语句:将代码字符串编译成V8的内部表示,并保存成一个Script对象;
第七条语句:执行编译后的内部表示,获得生成的结果。
首先通过编译器将源代码编译成抽象语法树:
不同于JavaScriptCore引擎,V8引擎并不将抽象语法树转变成字节码,而是通过JIT编译器的全代码生成器从抽象语法树直接生成本地代码。
其过程中的主要类图如下:
Script:表示的是JavaScript代码,既包含源代码,又包含编译之后生成的本地代码,所以它既是编译入口,又是运行入口;
Compiter:编译器类,辅助Script类来编译生成代码,它主要起一个协调者的作用,会调用解析器(Parse)来生成抽象语法树和全代码生成器,来为抽象语法树生成本地代码;
Parse:将源代码解析并构建成抽象语法树,使用AstNodeFactory类来创建他们,并使用Zone类来分配内存;
AstNode:抽象语法树节点类,是其他所有节点的基类;
AstVisitor:抽象语法树的访问者类,主要用来遍历抽象语法树;
FullCodeGenerator:AstVisitor类的子类,通过遍历抽象语法树来为JavaScript生成本地可执行的代码。
V8运行阶段的主要类图如下:
Script:前面介绍过,包含编译之后生成的本地代码,运行代码的入口;
Execution:运行代码的辅助类,包含一些重要的函数“call”,它辅助进入和执行Script中的本地代码;
JSFunction:需要执行的JavaScript函数表示类;
Runtime:运行本地代码的辅助类,主要提供运行时各种辅助函数;
Heap:运行本地代码需要使用的内存堆;
MarkCompactCollector:垃圾回收机制的主要实现类,用来标记,清除和整理等基本的垃圾回收过程;
SweeperThread:负责垃圾回收的线程。
V8中代码的执行过程如下图:
四
V8引擎所做优化
1. 优化回滚
Crankshaft编译器主要针对热点函数进行优化,它是基于JS源码分析的,而不是本地代码。为了性能考虑Crankshaft编译器会进行一些乐观的预测,认为这些代码比较稳定,变量类型不会发生变化,所以能够生成高效的本地代码。然而进行优化之后,V8发现并不是最优的,会执行优化回滚操作。
2. 隐藏类
将对象划分成不同的组,相同的组内对象拥有相同的属性名和属性值,组内的所有对象贡献该信息:
实例中对象a和b包含相同的属性名,V8就会将其归为同一个组,也就是隐藏类。这些属性在隐藏类中有相同的偏移值,这样,对象a和b可以共享这个类型信息,当访问这些对象属性的时候,根据隐藏类的偏移值就可以知道它们的位置并进行访问。
3. 内存管理
V8使用堆来管理JavaScript使用的数据,以及生成的代码、哈希表等;为了更方便地实现垃圾回收,同很多虚拟机一样,V8将堆分成三个部分,第一个是年轻分代,第二个是年老分代,第三个是大对象保留的空间;如下图:
4. 快照(Snapshot)
V8引擎开始启动的时候,需要加载很多内置的全局对象,同时也要建立内置的函数,比如Array、String、Math等。为了让引擎更加整洁,加载对象与建立函数等任务都是使用JS文件来实现的,V8引擎负责在编译和执行输入的JavaScript代码之前,先加载他们。
快照机制就是将一些内置的对象和函数加载之后的内存保存并序列化。序列化之后的结果很容易被反序列化,经过快照机制的启动时间,可以缩短启动时间。快照机制也能够将开发者认为需要的JS文件序列化,减少以后处理的时间。
5. 绑定和扩展
V8提供两种机制来扩展引擎的能力,第一是Extension机制,就是通过V8提供的基类Extension来达到扩展JavaScript能力的目的。第二是绑定,使用IDL文件或者接口文件来生成绑定文件,然后将这些文件同V8引擎代码一起编译。
五
实践——写JavaScript需要注意地方
1. 不要破坏隐藏类
建议:在构造函数中初始化所有对象成员,不要在以后更改类型;以相同的顺序初始化对象成员。
2. 数据表示
在V8中,数据的表示分成两个部分,第一个部分是数据的实际内容,它们是变长的;第二部分是数据的句柄,句柄的大小是固定的,句柄中包含指向数据的指针。为什么要这样设计呢?主要是因为V8需要进行垃圾回收,并需要移动这些数据内容,如果直接使用指针的话就会出问题或者需要比较大的开销,使用句柄的话就不存在这些问题,只需要将句柄中的指针修改即可。
具体的定义如下:
一个Handler的大小是4字节(32位机器),整数直接从value_
中获取值,而无需从堆中分配,然后分配一个指针指向它,这可以减少内存的使用并增加数据的访问速度。
所以,对于数值来说,只要能够使用整数的,尽量不要使用浮点数。
3. 数组初始化
建议:初始化使用数组常量小型固定大小的数组;
不要储存在数字数组非数字值(对象);
不要删除数组中的元素,尤其是数字数组;
不要装入未初始化或删除元素。
4. 内存
对引用不再使用的对象的变量设置为空(a=),引入delete关键字,删除无用对象。
5. 优化回滚
不要书写出触发优化回滚的代码,否则会大幅降低代码的性能。执行多次之后,不要出现修改对象类型的语句。
*请认真填写需求信息,我们会在24小时内与您取得联系。