眾所周知,flutter官方的webview存在這樣那樣的bug,一個個去維護又很累,還有一些是國內特有機型會產生的bug,特有輸入法產生的bug。像這種不會產生crash的bug,在isssues里提了,多半就沒有消息了。而且flutter官方也說webview不建議投入使用,鍵盤的坑也格外的多,無法復制粘貼之類的。對這一類做了一些適配,到最后百度輸入法的一個bug切切實實的打敗了我。也沒有別的方案可以解決。
而且flutter webview的bug其實是來自於androidView,甚至可能是flutter engine的問題,國內的大公司類似咸魚都是自己定制了flutter engine。所以,這塊選擇是用原生的webview來實現吧。
廢話有點多,簡單的介紹一下,我這里的業務場景,我現在的軟件是提供webview給第三方,有點類似小程序的樣子,然后也定制了一些功能。
實現類似小程序的樣子
像微信,支付寶之類的小程序打開后,后台會多出一個任務來,可以同時打開主任務和小程序的任務進行交互。不是很清楚的話,可以自己打開下手機看一下。
想要實現兩個任務其實很簡單,更改AndroidManifest里taskAffinity屬性,如下圖,
然后在啟動的時候設置intent屬性
intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);
這兩段結合起來,在startActivity的時候,他會新開一個棧,而不是和主任務在一個棧里。這樣就初步實現了后台中兩個任務的需求了。但是,現在問題來了,那如果他開了第二個小程序了怎么辦呢?對吧,看一下微信發現他會再創建一個。
好,那么問題來了,微信的小程序是不是開幾個創建幾個呢?開多少個后台就有多少個呢。我覺得不是,微信應該設置了數量,有個上限 ,但是這個上限是多少可以自己去設置一下。我在觀察微信小程序的activity的時候發現,不同的小程序特定情況下會占用同一個的activity。
竟然如此,那么就是說,微信小程序和activity的按需分配的,也就是說小程序是無限的,但是activity是有限的。當要打開一個小程序前,他去看看有哪些activity是空着的,空着就把他塞進去。
所以這邊,我的解決方法是創建多個webviewActivity,他們分屬不同的棧,定了一下上限為5。也就是創建了五個一樣的activity,當然存在一些小區別。實現的邏輯大概如下:
我在application申明了一個數組,List[0]即表示activity0,當list[0]中有數據的時候 ,即表示這個activity在使用中。list在application啟動的時候會自動清零,然后依次存進去,一邊存一邊啟動這個activity。5個存滿之后,加個標識,從頭開始覆蓋。因為主項目是flutter寫的,所以很多功能無法剝離開,必須要啟動主項目,所以在application里再去申明一個int類型的activity,當webview里的頁面需要調用主項目的方法時,把當前activity記錄下來,比如activity0,就記錄0,然后等主項目這邊結束了,就啟動0這個activity。
webview實現input type=file
flutter的webview是沒有這個功能的,因為原生的webview也沒有這個功能,需要重寫onShowFileChooser這個方法,這一塊我之前寫過一篇比較詳細的,可以看下這篇https://www.cnblogs.com/afei123/p/13053712.html。
簡單說一下,那邊的邏輯是在onShowFileChooser跳轉一個透明activity,然后從這個activity去選擇,如下圖。然后打開相機和選擇文件這塊可以去那篇文章的github鏈接里去看,具體不贅述了。
當初為了下面這個dialog,新建了一個ativity,其實現在自定義webview的時候完全可以放到自己的webviewactivity里去。這里講點高級的東西,需求來了,希望打開文件管理器可以根據前端代碼進行選擇。變成下圖這樣。
input type=file的時候還可以設置accept屬性,用來選擇打開照片還是別的什么東西,打開照片的情況下,文件什么都是不可看的狀態。然后accept這個屬性可以通過下面的方法獲取,所以怎么搞明白了吧。
final String[] acceptTypes = getSafeAcceptedTypes(fileChooserParams);
實現文檔預覽
文檔預覽這塊實現需要接入TBS服務,也就是騰訊的x5內核。這個有一個缺陷就是需要把文件下載到本地,而且如果不是用騰訊瀏覽器打開只支持9種格式,具體的可以在TBS的官網去看一下。
我這邊將原生的webview也全部替換成了x5,也為了方便點。
所以實現文檔預覽需要走兩步,一步下載,一步打開。下載文檔自己就根據url去設置就好了,開一個線程,下載完了執行打開文檔的方法。
我這里使用的是TbsReaderView這個方法,這個方法官網上沒講。使用方法如下
tbsReaderView = new TbsReaderView(this,readerCallback); RelativeLayout mRelativeLayout = findViewById(R.id.tbsView); mRelativeLayout.addView(tbsReaderView,new RelativeLayout.LayoutParams(-1,-1));
我這邊是創建了一個activity來顯示他,然后自己定義了下toolbar。
圖片預覽
然后還要實現的就是圖片預覽,我這邊是默認拿到了圖片地址list。然后用photoView+viewPager實現他。不過很奇怪,當photoView放入viewPager之后,就無法縮放了。這塊mark一下。
和flutter的交互
如下圖,當反饋需要打開flutter頁面怎么辦。
包括js事件需要傳遞給flutter怎么辦,在flutter端注冊一個MethodChannel,然后在flutter這邊設置一個接收端。具體實現操作就是這樣了。
如果對具體細節代碼實現有問題的話可以私信我。q980160988