开启生长之旅!这是我参与「日新计划 2 月更文应战」的第 2 天,点击查看活动详情

前言

在开发webview的loading作用的时分会有一些问题,这边记录一些碰到的常见的问题,并且规划出一套Loading的计划来处理相关的问题。

1. loading的挑选

开发loading作用的原因在于webview加载页面的时分,有时分会耗时,导致不显现内容又没有任何提示,作用不太好,所以需求在webview运用的当地加上loading的作用,其实更好的体会是还要加上EmptyView,我这边首要就以loadingView来举例。

那开发这loading基本有两种办法,一种是运用window,也便是Dialog这些弹窗的办法,在加载时弹出弹窗,在加载完毕后封闭弹窗,有些人或许会封装好一些loading弹窗,然后在这儿复用。
这个办法的优点是假如你封装好了,能直接复用,省去许多代码。缺陷也很明显,弹窗弹出的时分是否处于一个不允许交互的状况,假如这个流程有问题,那便一直无法和页面做交互

另一种办法是直接在webview的上层覆盖一个LoadingView,webview是承继FrameLayout,便是也能够直接addView。
这个办法的优点便是不会呈现上面的问题,因为我webview地点的页面封闭了,它的loading也会跟着一起消失,并且显现的作用会好一些。缺陷便是或许一些特别的webview你会单独做操作,导致会多写一些代码

没有说哪种办法是完成会比较好,首要看运用的场景和具体的需求。

2. loading显现机遇的问题

咱们做loading的思路便是加载开端的时分显现,加载完成之后封闭,那挑选这个开端的机遇和完毕的机遇就比较重要了。

大多数人都会直接运用WebViewClient的onPageStarted回调作为开端机遇,把onPageFinished的回调,觉得直接这样写就行了,无所谓,横竖webview会出手。

这个思路确实能在正常的状况下显现正常,可是在弱网状况下呢?杂乱的网络环境下呢?有些人或许也会碰到一些这样的状况,loading的show写在onPageStarted中,加载时会先白屏一下,才开端显现loading,可是这个白屏的时刻很短,所以觉得无所谓。但有没有想过这在正常网络环境下的白屏一下放到杂乱的有问题的网络环境中会被放大成什么样。

这个加载过程其实大体分为两个阶段,从loadurl到WebViewClient的onPageStarted和从WebViewClient的从onPageStarted到onPageFinished

所以我的做法是在loadurl的时分去start loading,而不是WebViewClient的onPageStarted回调的时分。

这个是开端的机遇,那完毕的机遇会不会有问题,还真或许有,有时分你会发现一种现象,加载完之后,你的H5内容和loading会一起显现一段时刻,才封闭loading(几年前有碰到过,写这篇文章的时分测试没有复现过,不知道是不是版别更新修正了这个问题)

那假如碰到这个问题该怎样处理呢?碰到这个问题,阐明onPageFinished的回调机遇在页面加载完之后,所以不可信。咱们知道除了这个办法之外,BaseWebChromeClient也有个办法onProgressChanged表示加载的进展,当然这个进展你拿去判别也会有问题,因为它并不会每次都会回调100给你,或许有时分给你96,就没了。
我曾经的做法是双重判别,判别是进展先回来>85仍是onPageFinished先调用,只需有一个调用,我都会封闭loading

3. 体会优化

当然处理好显现的封闭的机遇还不可,想想假如在loadurl中show loading会怎样,没错,就算网速快的状况,页面让loading一闪而过,那这样所造成的体会就很欠好,所以咱们需求做一个推迟显现,我个人习惯是推迟0.5秒。当然推迟显现也会有推迟显现的问题,比如推迟到0.3秒的时分你封闭页面怎样办,再0.2秒之后我总不不能让它显现吧。

说了显现,再说封闭。无论是onPageFinished办法仍是onProgressChanged,你能确保它一定会有回调吗?这些代码都不是可控的,里边会不会呈现既没抛反常,也没给回调的状况。或许有人说不会的,我都用了这么多年了,没呈现过这种问题,可是已然不是咱们可控的代码,加一层稳妥总没错吧。
其实这也简单,定一个timeout的逻辑就行,我个人是定义10秒超时时刻,假如10秒后没有封闭loading,我就手动封闭并显现emptyview的error页面。这个超时时刻仍是比较有用,最上面说了loading的挑选,假如你的loading做成view,那即便没有这个逻辑也影响不大,最多就会菊花一直转,但假如你是window做的,没有超时的处理,又没有回调,那你的window会一直显现卡住页面。

4. loading终究规划作用

根据上面的状况,我写个Demo,首先loading的挑选,我挑选根据view,所以要写个自定义View

public class WebLoadingView extends RelativeLayout {
    private Context mContext;
    // 0:正常状况;1:loading状况;2:显现loadingview状况
    private AtomicInteger state;  
    private Handler lazyHandler;
    private Handler timeOutHandler;
    public BaseWebLoadingView(Context context) {
        super(context);
        init(context);
    }
    public BaseWebLoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    public BaseWebLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
    private void init(Context context) {
        this.mContext = context;
        state = new AtomicInteger(0);
        lazyHandler = new Handler(Looper.getMainLooper());
        timeOutHandler = new Handler(Looper.getMainLooper());
        initView();
    }
    private void initView() {
        LayoutInflater.from(mContext).inflate(R.layout.demo_loading, this, true);
    }
    public void show() {
        if (state.compareAndSet(0, 1)) {
            lazyHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (state.compareAndSet(1, 2)) {
                        setVisibility(View.VISIBLE);
                    }
                }
            }, 500);
            timeOutHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    close();
                }
            }, 10000);
        }
    }
    public void close() {
        state.set(0);
        setVisibility(View.GONE);
        try {
            lazyHandler.removeCallbacksAndMessages(null);
            timeOutHandler.removeCallbacksAndMessages(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码应该都比较好了解,就不过多介绍了,然后在自定义webview的loadurl里边展现

@Override
public void loadUrl(String url) {
    if (webLoadingView != null && !TextUtils.isEmpty(url) && url.startsWith("http")) {
        webLoadingView.show();
    }
    super.loadUrl(url);
}

写这儿首要是有个当地要注意,便是调办法时也会履行这个loadUrl,所以要判别是加载网页的时分才显现loading。

5. 总结

总结几个重点吧,第一个是对第三方的东西(webview这个也相似第三方吧,坑真的许多),咱们没办法把控它的流程,或者说没办法把控它的生命周期,所以要封装一套流程逻辑去给调用端方便去运用。
第二个问题是版别的问题,或许会呈现不同的版别所表现的作用不同,这个是需求留心的。

假如要完美处理这堆loading相关的问题,最好的办法便是看源码,你知道它里边是怎样完成的,为什么会呈现onPageStarted之前还会有一段间隔时刻,那就去看loadUrl和onPageStarted回调之间的源码,看它做了什么操作嘛。我个人是没看源码,所以这儿只能说是浅谈。