文章

WebView相关问题

WebView相关问题

WebView 的内核

WebView 内核加载逻辑

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
private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
    "/data/misc/shared_relro/libwebviewchromium32.relro";
private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
    "/data/misc/shared_relro/libwebviewchromium64.relro";
/**
  * Load WebView's native library into the current process.
  *
  * <p class="note"><b>Note:</b> Assumes that we have waited for relro creation.
  *
  * @param clazzLoader class loader used to find the linker namespace to load the library into.
  * @param libraryFileName the filename of the library to load.
  */
public static int loadNativeLibrary(ClassLoader clazzLoader, String libraryFileName) {
    if (!sAddressSpaceReserved) {
        Log.e(LOGTAG, "can't load with relro file; address space not reserved");
        return WebViewFactory.LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
    }

    String relroPath = VMRuntime.getRuntime().is64Bit() ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
    CHROMIUM_WEBVIEW_NATIVE_RELRO_32;
    int result = nativeLoadWithRelroFile(libraryFileName, relroPath, clazzLoader);
    if (result != WebViewFactory.LIBLOAD_SUCCESS) {
        Log.w(LOGTAG, "failed to load with relro file, proceeding without");
    } else if (DEBUG) {
        Log.v(LOGTAG, "loaded with relro file");
    }
    return result;
}

Android 免安装升级 WebView 内核

WebView 存在什么问题?及怎么解决?

WebView 崩溃?

WebView 的优化

WebView 预加载

WebView 多进程

WebView 资源存本地

通用拦截 - 缓存共享、请求并行 直出解决了文字展现的速度问题,但是图片加载渲染速度还不理想。 借由内核的 shouldInterceptRequest 回调,拦截落地页图片请求,由客户端调用图片下载框架进行下载,并以管道方式填充到内核的 WebResourceResponse 中。就是说在 shouldInterceptRequest 拦截所有 URL,之后只针对后缀是.PNG/.JPG 等图片资源,使用第三方图片下载工具类似于 Fresco 进行下载并返回一个 InputStream。

WebView 秒开(WebView 白屏)

网页加载缓慢,白屏,使用卡顿。

为何有这种问题?

  1. 调用 loadUrl() 方法的时候,才会开始网页加载流程
  2. js 臃肿问题
  3. 加载图片太多
  4. webview 本身问题

白屏问题如何监控?

什么时候会出现白屏?

  1. 资源加载错误:尤其是 JS 资源加载异常时
  2. 页面逻辑问题
  • 读取 undefined null 的属性,null.a;
  • 对普通对象进行函数调用,const o = {}; o();
  • 将 null undefined 传递给 Objects.keys,Object.keys(null);
  • JSON 反序列化接受到非法值,JSON.parse({});
  1. 接口异常导致的白屏:页面数据依赖网络接口,且页面没有默认的初始数据,导致在网络不好的情况下,接口数据没有获取到,从而导致页面列表数据空白等问题。

如何判断是否白屏?

判断屏幕元素是否为白屏元素

Webiew 是怎么加载网页的呢?

Webview 初始化→DOM 下载→DOM 解析→CSS 请求/下载→CSS 解析→渲染→绘制→合成

优化方向

1、提前内核初始化

1
2
3
4
5
6
7
8
public class App extends Application {
    private WebView mWebView ;
    @Override
    public void onCreate() {
        super.onCreate();
        mWebView = new WebView(new MutableContextWrapper(this));
    }
}

2、Webview 复用池

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
public class WebPools {
    private final Queue<WebView> mWebViews;
    private Object lock = new Object();
    private static WebPools mWebPools = null;
    private static final AtomicReference<WebPools> 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();
        }
    }
}

带来的问题:内存泄漏

3、独立进程,进程预加载

启动 webview 页面前,先启动 PreWebService 把 web 进程创建了,当启动 WebActivity 时,系统发发现 web 进程已经存在了,就不需要花费时间 Fork 出新的 web 进程了。

4、使用 x5 内核,替换原生的浏览器内核

5、app 内置资源

app 内置 css、js 文件并控制版本

6、三方框架和大厂方案

  1. VasSonic VasSonic 的核心思想: 并行,充分利用 webview 初始化的时间进行一些数据的处理。在包含 webview 的 activity 启动时会一边进行 webview 的初始化逻辑,一边并行的执行 sonic 的逻辑。这个 sonic 逻辑就是网页的预加载

  2. 百度 app 方案
  3. 今日头条方案
本文由作者按照 CC BY 4.0 进行授权