热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WebView性能和用户体验优化

回顾系统WebView进化史从Android4.4系统开始,Chromium内核取代了Webkit内核。从Android5.0系统开始,WebView移植成了一个独立的apk,可以

回顾系统 WebView 进化史

  • 从Android4.4系统开始,Chromium内核取代了Webkit内核。
  • 从Android5.0系统开始,WebView移植成了一个独立的apk,可以不依赖系统而独立存在和更新。
  • 从Android7.0 系统开始,如果用户手机里安装了 Chrome , 系统优先选择 Chrome 为应用提供 WebView 渲染。
  • 从Android8.0系统开始,默认开启WebView多进程模式,即WebView运行在独立的沙盒进程中。

随着技术的发展 , Google 推出了 PWA Web 形态App ,微信推出小程序 ,Facebook 推出 React , 前端变得越来越广泛(复杂的前端环境) , 所以移动端的 Web 性能变得越来越重要 , 虽然随着 Google 不断的对 WebView 内核升级 , 性能也跟上了脚步 ,但是在移动端还是有很多方面值得我们去优化 。

内核初始化

第一次打开 Web 页面 , 使用 WebView 加载页面的时候特别慢 ,第二次打开就能明显的感觉到速度有提升 ,为什么 ? 是因为在你第一次加载页面的时候 WebView 内核并没有初始化 , 所以在第一次加载页面的时候需要耗时去初始化 WebView 内核 。提前初始化 WebView 内核 ,例如如下把它放到了 Application 里面去初始化 , 在页面里可以直接使用该 WebView

public class App extends Application {
private WebView mWebView ;
@Override
public void onCreate() {
super.onCreate();
mWebView = new WebView(new MutableContextWrapper(this));

}
}

复用 WebView

复用思想在移动端是一种很重要的思想 , 像 ListView ,RecyclerView 复用子View 一样 , 大大提高了性能和节俭内存 , 如果你大量使用 WebView 那么我建议你可以考虑一下复用 WebView , 如果你的应用只是在某些页面使用了 WebView 那么我建议你放弃复用 WebView , 因为复用 WebView 并不会给你带来多大的性能提升而且会带来一些问题 ,而且在内存吃紧移动端 ,内存显得特别珍贵 , 下面给出一些测试代码和数据。

验证复用 WebView 和提前初始化 WebView 必要性

private void testWebViewInitUsedTime(){
long p = System.currentTimeMillis();
WebView mWebView = new WebView(this);
long n = System.currentTimeMillis();
Log.i("Info", "testWebViewFirstInit use time:" + (n-p));
}

testWebViewInitUsedTime();
testWebViewInitUsedTime();

//测试环境 Android 7.0 三星S7
testWebViewFirstInit use time:182
testWebViewFirstInit use time:4

上面是测试 WebView 初始耗时的一些代码 , 可以看出第一次提前初始化还是很有必要的 , 第二初始化只耗时 4 毫秒 , 也就是说一般情况创建一个 WebView 只需要4毫秒 ,如果单纯几个页面是复用 WebView 这种优化意义不大 , 因为稍微处理不妥当就会出现泄漏 。

下面给出复用 WebView 的一些关键代码

public class WebPools {
private final Queue mWebViews;
private Object lock = new Object();
private static WebPools mWebPools = null;
private static final AtomicReference mAtomicReference = new AtomicReference<>();
private static final String TAG=WebPools.class.getSimpleName();
private WebPools() {
mWebViews = new LinkedBlockingQueue<>();
}
public static WebPools getInstance() {
for (; ; ) {
if (mWebPools != null)
return mWebPools;
if (mAtomicReference.compareAndSet(null, new WebPools()))
return mWebPools=mAtomicReference.get();
}
}
public void recycle(WebView webView) {
recycleInternal(webView);
}
public WebView acquireWebView(Activity activity) {
return acquireWebViewInternal(activity);
}
private WebView acquireWebViewInternal(Activity activity) {
WebView mWebView = mWebViews.poll();
LogUtils.i(TAG,"acquireWebViewInternal webview:"+mWebView);
if (mWebView == null) {
synchronized (lock) {
return new WebView(new MutableContextWrapper(activity));
}
} else {
MutableContextWrapper mMutableCOntextWrapper= (MutableContextWrapper) mWebView.getContext();
mMutableContextWrapper.setBaseContext(activity);
return mWebView;
}
}
private void recycleInternal(WebView webView) {
try {
if (webView.getContext() instanceof MutableContextWrapper) {
MutableContextWrapper mCOntext= (MutableContextWrapper) webView.getContext();
mContext.setBaseContext(mContext.getApplicationContext());
LogUtils.i(TAG,"enqueue webview:"+webView);
mWebViews.offer(webView);
}
if(webView.getContext() instanceof Activity){
// throw new RuntimeException("leaked");
LogUtils.i(TAG,"Abandon this webview , It will cause leak if enqueue !");
}
}catch (Exception e){
e.printStackTrace();
}
}
}

注意在 WebView 进入 WebPools 之前 , 需要重置 WebView ,包括清空注入 WebView 的注入对象 , 否则非常容易泄露。

WebView 独立进程 , 进程预加载 。

因为 WebView 内存泄露 , 以及多进程内存拓展 , 相信有一部分开发人员会把 WebView 放在一个独立的进程里面 , 那么第一次加载 WebView 页面 ,加上系统需要时间 Fork 出新进程 , 那么加载变得更慢了 , 因为进程的创建也是一件耗时的事情 , 所谓的预加载进程 , 就是提前把进程创建出来 , 提升加载速度 ,大致的做法如下

android:name=".PreWebService"
android:process=":web"/>
android:name=".WebActivity"
android:process=":web"
/>

其实不一定要 Service , 启动「web」 进程 Broadcast 广播也是可以的 , 提前在进入 WebView 页面之前 , 先启动 PreWebService 把 「web」 进程创建了 ,当系统在启动 WebActivity 的时候 , 系统发现了 「web」 进程已经创建存在了 , 系统就不需要耗费时间 Fork 出新的「web」进程了。

提前显示进度条

提前显示进度条不是提升性能 , 但是对用户体验来说也是很重要的一点 , WebView.loadUrl("url") 不会立马就回调 onPageStarted 或者 onProgressChanged 因为在这一时间段 , WebView 有可能在初始化内核 , 也有可能在与服务器建立连接 , 这个时间段容易出现白屏 , 白屏用户体验是很糟糕的 , 所以我建议

private void go(String url) {
this.mWebView.loadUrl(url);
this.mIndicator.show() //显示进度条
}

loadUrl 之后立马就把进度条显示出来 , 给用户一个明显视觉 。

开启软硬件加速

开启软硬件加速这个性能提升还是很明显的,但是会耗费更大的内存 。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else if (Build.VERSION.SDK_INT webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

总结

上面提出这些性能优化都不是那么完美无缺的,基本都会带来一部分系统资源的消耗 , 比如在 Application 里面提前初始化WebView , 虽然提升了 WebView 页面的启动速度, 但是缺拖慢了 App 的冷启动速度 ,独立进程和开启软硬件加速也都会带来内存更大的开销 ,所以凡事都是存在利和弊,至于在项目中利与弊怎么权衡,都是需要根据用户需求和各种因素来量度的。

最后

留下一个基于 WebView 的强大库的传送门 GitHub 。


推荐阅读
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 如何在HTML中获取鼠标的当前位置
    本文介绍了在HTML中获取鼠标当前位置的三种方法,分别是相对于屏幕的位置、相对于窗口的位置以及考虑了页面滚动因素的位置。通过这些方法可以准确获取鼠标的坐标信息。 ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
  • 本文整理了常用的CSS属性及用法,包括背景属性、边框属性、尺寸属性、可伸缩框属性、字体属性和文本属性等,方便开发者查阅和使用。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
author-avatar
尼古拉斯Ga
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有