網站首頁 編程語言 正文
前言
在開發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。
總結
總結幾個重點吧,第一個是對第三方的東西(webview這個也類似第三方吧,坑真的很多),我們沒辦法把控它的流程,或者說沒辦法把控它的生命周期,所以要封裝一套流程邏輯去給調用端方便去使用。
第二個問題是版本的問題,也許會出現不同的版本所體現的效果不同,這個是需要留意的。
如果要完美解決這堆loading相關的問題,最好的方法就是看源碼,你知道它里面是怎么實現的,為什么會出現onPageStarted之前還會有一段間隔時間,那就去看loadUrl和onPageStarted回調之間的源碼,看它做了什么操作嘛。我個人是沒看源碼,所以這里只能說是淺談。
原文鏈接:https://juejin.cn/post/7195393339691106364
相關推薦
- 2022-09-08 Python中LSTM回歸神經網絡時間序列預測詳情_python
- 2022-04-09 SpringBoot自定義Validated枚舉校驗器
- 2022-09-07 Python?CSV?文件解析和生成方法示例_python
- 2023-02-18 go?micro微服務框架項目搭建方法_Golang
- 2022-07-08 PyHacker編寫指南引用Nmap模塊實現端口掃描器_python
- 2022-07-22 git提交代碼設置某些文件不可上傳
- 2022-04-06 C語言函數調用的三種實現方法實例_C 語言
- 2023-02-04 C語言設計實現掃描器的自動機的示例詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支