![]()
1. Obtain the streaming output of the large model through the SSE interface and render it in real-time using WebView
Example of using evaluateJavaScript to update HTML content:
![]()
2. Multiple WebViews can be rendered simultaneously, supporting page switching similar to ViewPager2
The current plan can achieve:
1. WebView reuse saves time in creating WebViews
2. Avoid duplicate loading URLs to achieve data rendering in seconds
Release+R8+Local HTML+Personal Machine
| Type/Time | Until creation completion (average) | To start rendering (average) |
|---|---|---|
| No cache for the first time | 104ms | 230ms |
| No cache follow-up | 3ms | 64ms |
| cached | 0ms | 2ms |
Minimalist WebViewCache.kt
![]()
JsWithCacheWebFragment.kt
![]()
The creation and related operations of WebViews must be done in the main thread, which is mandatory by Android. We can only create them in advance, put them in the cache pool, retrieve them from the cache pool when in use, and release them to the cache pool in a timely manner when not in use. There is currently no other way to optimize.
In fact, the initial creation process may take longer, while the loadURL is consistently slower. Testing local HTML files takes about 50ms at the fastest, so avoiding calling this method is the key to optimizing time consumption.
The internal mechanism of Android Webview determines it, which can be determined by using WebView. progress==100
Without preheating in advance, the creation of WebViews and laodURLs cannot be avoided during the first use, and can only be loaded in advance.
Preloading requires determining the appropriate timing, which is generally recommended to be done through idleHeader.
![]()
Then call at the appropriate time
Looper.getMainLooper().queue.addIdleHandler(WebViewCache.preloadIdleHandler)
Mainly the context and WebViewClient, we can reset them when releasing.
Mainly related to the content of the rendered page, WebView itself does not actually occupy a lot of memory, and multiple instances will not cause a linear increase in memory usage.
You can reduce memory by optimizing code logic and adjusting cache pool size.
I have always heard that WebMessagePort performs better than JSInterface, but I did not see any performance difference in this minimalist scenario on the demo. Perhaps the interaction frequency is still not enough. ChatGPT says it is suitable for transmitting audio and video streams.
ViewPager2 is actually implemented using RecyclerView internally, and its nested sliding needs to be disabled first. Then, because the content page needs to be linked with BottomSheet, NestdScrollView needs to be used.
![]()
Additionally, there may be a situation where nested sliding is not possible after switching tabs, which is a known issue with BottomSheet and is detailed in this analysis article. However, this problem was not encountered in the demo.
I strongly recommend reading this ViewPager2 analysis article. Simply put:
Because ViewPager2 uses RecyclerView internally, and the default for quick switching is SmoothScrollTo, this may cause tabs passing by along the way to be quickly loaded and then quickly destroyed. When the logic becomes complex, rebuilding and destroying five or six tabs at the same time will naturally deteriorate the performance.
To avoid this issue while also supporting left and right switching, you can:
Determine whether it is scrollTo or smoothscrollTo by checking the distance between the current position and the target position when clicking
When clicked, scroll directly to
The BottomSheetBehavior of BottomSheetDialog is added to the FrameLayout, and its default measurement behavior is to determine its own height based on the maximum value of childView, and it will ignore the highMeasureSpec passed from the upper layer.
So in the demo, we customized the onMeasure method for FrameLayout, fixed the height to the maximum value, and then stuffed ViewPager2 into this FrameLayout.








