package com.example.kanban
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.WindowManager
import android.webkit.*
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
private val handler = Handler(Looper.getMainLooper())
private var timeoutRunnable: Runnable? = null
private var isMonitoringRequests = false
private var pendingRequests = 0
private var lastRequestTime: Long = 0
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 启用硬件加速
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
webView = findViewById<WebView>(R.id.webView).apply {
// 基础设置
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
allowContentAccess = true
allowFileAccess = true
setSupportZoom(true)
builtInZoomControls = true
displayZoomControls = false
cacheMode = WebSettings.LOAD_DEFAULT
useWideViewPort = true
loadWithOverviewMode = true
// 解决ORB和安全问题
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
allowUniversalAccessFromFileURLs = true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
safeBrowsingEnabled = false
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
}
}
// 添加JavaScript接口
addJavascriptInterface(WebAppInterface(), "Android")
webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: android.graphics.Bitmap?) {
super.onPageStarted(view, url, favicon)
startRequestMonitoring()
}
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
injectRequestMonitoringScript()
}
override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
super.onReceivedError(view, request, error)
handleWebError("加载错误: ${error.description}")
}
override fun onReceivedHttpError(
view: WebView,
request: WebResourceRequest,
errorResponse: WebResourceResponse
) {
super.onReceivedHttpError(view, request, errorResponse)
handleWebError("HTTP错误: ${errorResponse.statusCode}")
}
// 捕获URL变化
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
if (request != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d("WebView", "Loading URL: ${request.url}")
// 这里可以添加处理URL变化的逻辑
}
return super.shouldOverrideUrlLoading(view, request)
}
}
}
loadUrlWithRetry(
"http://192.168.16.69:10011/#/show-draw?v=T1AnDCLWYo1fi0Ngzg5rW47U9W5joYwQ2F8aK1ENVaErtPWTiOlCN5y63ZsMTQObmlqGVKjmUceKYcvRfg%2BSXA%3D%3D&satoken=9a4d0de4-927d-41a0-b5c5-4440a0ddaae0",
maxRetries = 2
)
}
private fun startRequestMonitoring() {
isMonitoringRequests = true
pendingRequests = 0
timeoutRunnable?.let { handler.removeCallbacks(it) }
timeoutRunnable = Runnable {
if (isMonitoringRequests && pendingRequests > 0) {
handleWebError("请求超时,仍有 $pendingRequests 个请求未完成")
}
}.also {
handler.postDelayed(it, 30000) // 30秒超时
}
}
private fun injectRequestMonitoringScript() {
webView.evaluateJavascript("""
(function() {
if (!window.requestTracker) {
window.requestTracker = {
count: 0,
completed: 0,
activeRequests: new Set(),
trackRequest: function(id) {
this.count++;
this.activeRequests.add(id);
Android.requestStarted(id);
return function(success, id) {
this.completed++;
this.activeRequests.delete(id);
Android.requestCompleted(id, success);
}.bind(this);
},
// 获取当前所有活跃请求
getActiveRequests: function() {
return Array.from(this.activeRequests);
}
};
// Hook XMLHttpRequest
var originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
var requestId = 'xhr_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
var tracker = window.requestTracker.trackRequest(requestId);
this.addEventListener('loadend', function() {
tracker(this.status >= 200 && this.status < 300, requestId);
}.bind(this));
originalOpen.apply(this, arguments);
};
// Hook fetch API
var originalFetch = window.fetch;
window.fetch = function(input, init) {
var requestId = 'fetch_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
window.requestTracker.trackRequest(requestId);
return originalFetch.apply(this, arguments)
.then(function(response) {
window.requestTracker.activeRequests.delete(requestId);
Android.requestCompleted(requestId, response.ok);
return response;
})
.catch(function(error) {
window.requestTracker.activeRequests.delete(requestId);
Android.requestCompleted(requestId, false);
throw error;
});
};
// 监控资源加载
function observeResourceLoading() {
// 监听现有资源
document.querySelectorAll('img, script, link[rel="stylesheet"]').forEach(function(el) {
if (!el.complete) {
var requestId = el.tagName.toLowerCase() + '_' + Date.now() + '_' + el.src;
var tracker = window.requestTracker.trackRequest(requestId);
el.addEventListener('load', function() {
tracker(true, requestId);
});
el.addEventListener('error', function() {
tracker(false, requestId);
});
}
});
// 监听新添加的资源
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes) {
mutation.addedNodes.forEach(function(node) {
if (node.tagName && ['IMG', 'SCRIPT', 'LINK'].includes(node.tagName)) {
if (node.tagName === 'LINK' && node.rel !== 'stylesheet') return;
var requestId = node.tagName.toLowerCase() + '_' + Date.now() + '_' + (node.src || node.href);
var tracker = window.requestTracker.trackRequest(requestId);
node.addEventListener('load', function() {
tracker(true, requestId);
});
node.addEventListener('error', function() {
tracker(false, requestId);
});
}
});
}
});
});
observer.observe(document, {
childList: true,
subtree: true
});
}
// 页面加载完成后开始监控资源
if (document.readyState === 'complete') {
observeResourceLoading();
} else {
window.addEventListener('load', observeResourceLoading);
}
}
})();
""".trimIndent(), null)
}
private fun loadUrlWithRetry(url: String, maxRetries: Int, currentRetry: Int = 0) {
if (currentRetry > 0) {
Toast.makeText(this, "正在重试 ($currentRetry/$maxRetries)...", Toast.LENGTH_SHORT).show()
}
webView.loadUrl(url)
handler.postDelayed({
if (webView.progress < 90 && currentRetry < maxRetries) {
loadUrlWithRetry(url, maxRetries, currentRetry + 1)
}
}, 10000) // 10秒后检查
}
private fun handleWebError(message: String) {
runOnUiThread {
webView.loadData("""
<html>
<body style='font-family:sans-serif;padding:20px;text-align:center;'>
<h3 style='color:#d32f2f;'>加载遇到问题</h3>
<p>$message</p>
<button onclick="window.location.reload()"
style='padding:10px 20px;background:#4285f4;color:white;
border:none;border-radius:5px;margin-top:20px;'>
重新加载
</button>
</body>
</html>
""", "text/html", "UTF-8")
}
}
inner class WebAppInterface {
@JavascriptInterface
fun requestStarted() {
pendingRequests++
lastRequestTime = System.currentTimeMillis()
Log.d("WebView", "Request started. Pending: $pendingRequests")
}
@JavascriptInterface
fun requestCompleted() {
pendingRequests--
Log.d("WebView", "Request completed. Pending: $pendingRequests")
if (pendingRequests <= 0) {
handler.post {
isMonitoringRequests = false
timeoutRunnable?.let { handler.removeCallbacks(it) }
Toast.makeText(this@MainActivity, "所有内容加载完成", Toast.LENGTH_SHORT).show()
// 请求全部完成后,执行JavaScript刷新页面或通知页面更新
updatePageContent()
}
}
}
}
private fun updatePageContent() {
// 方法1: 执行JavaScript通知页面数据已更新
webView.evaluateJavascript("""
(function() {
// 检查页面是否有更新数据的函数
if (window.onDataUpdated) {
window.onDataUpdated();
} else {
// 如果没有特定函数,尝试重新加载页面元素
document.dispatchEvent(new Event('dataUpdated'));
}
})();
""".trimIndent(), null)
// 方法2: 可选的页面刷新(谨慎使用,可能导致无限循环)
// webView.reload();
}
override fun onDestroy() {
handler.removeCallbacksAndMessages(null)
webView.stopLoading()
webView.destroy()
super.onDestroy()
}
} 打开的页面包含ajax请求,在电脑端浏览器都正常显示,但是在app上,部分接口没等返回数据,页面就加载完成了
最新发布