V1.4.9: 修复启动被误判为"断网恢复"(并补齐 WebView 失效原因取证)
现场日志(1.4.8)显示启动瞬间就出现: 20:46:59.636 NET-UP 设备网络已恢复, 重建歌曲源会话 20:46:59.647 NET-REFRESH reason=net-up retried=0 20:46:59.980 BOOT 即 NET-UP 出现在 BOOT 之前 —— 启动被当成了"断网后恢复"。 根因: KtvNetworkMonitor.start() 末尾无条件 onChange(initial), 注册 ConnectivityManager 回调时系统会回报一次初始状态,上层把它当成了状态跃迁。 而启动流程(onCreate)已经做过 startRemoteServer()/updateMobileQrOverlay()/ refreshLogDeviceContext()/DeviceMac 解析,所以这次"恢复"是纯粹重复劳动,且有副作用: - SongApiClient.resetSourceSession() 白清掉刚建立的歌曲源会话; - Eac19WindowGuard.noteUrlOk() 语义错误(还没取到任何地址就标记窗口正常, 可能掩盖真正的歌曲源窗口到期提示); - 状态栏被写成"网络已恢复"(启动瞬间就显示,用户可见的错误文案)。 修复: - KtvNetworkMonitor.start/refresh 的回调签名改为 (online, initial), initial=true 表示"注册时的初始状态"; - MainActivity.onNetworkStateChanged(online, initial): 初始状态**只更新断网角标**, 不做任何恢复动作;恢复动作(重建会话/重启点歌服务/刷新二维码/重试下载) 只在真正的 offline→online 跃迁时执行。 同时补齐 WebView 失效原因的取证(旧日志只有"KtvApi 未导出"这一句,无法定因): - wrapHtml 注入特性探测: 上报 WEBVIEW ua=... asyncFunction=yes|no promise=... (用 new Function 探测,避免探测语句自身引起语法错误)。Android 5.x 的 WebView 可能是 Chrome 39~4x,不支持 async/await 会让**整个 ktv_api.js 语法报错** → window.KtvApi 未定义、桥永远不就绪,这条日志可直接确证; - window.onerror 上报脚本解析/运行错误(JS-ERROR ... @line:col),语法错误也会触发; - 合并重复的失败行: "JS-BRIDGE 初始化返回 false" 与 "JS-BRIDGE 判定本机 WebView 不可用" 只保留后者(含原因)。 版本 1.4.8(28) 到 1.4.9(29);签名证书不变。ktv_api.js 未改动,KTV_BRIDGE_API 仍为 8。
This commit is contained in:
+2
-2
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "com.local.ktv"
|
||||
minSdk 21
|
||||
targetSdk 25
|
||||
versionCode 28
|
||||
versionName "1.4.8"
|
||||
versionCode 29
|
||||
versionName "1.4.9"
|
||||
ndk {
|
||||
// 双 ABI 打包:兼容 64 位(Exynos 1580 等 arm64-v8a)与 32 位设备(极米 Play 超越版
|
||||
// 的 Amlogic 950X2/S905X2 采用 32 位 Android 用户态,必须包含 armeabi-v7a,否则
|
||||
|
||||
@@ -388,11 +388,28 @@ class KtvJsBridge(context: Context) {
|
||||
|
||||
private fun wrapHtml(script: String): String {
|
||||
val escaped = script.replace("</script", "<\\/script", ignoreCase = true)
|
||||
// console.log 桥接:ktv_api.js 的 _log()/_log 细节(Try source / Token FAIL / do.php code=
|
||||
// 等)原本只进 WebView 控制台,远程日志里完全看不到,点歌失败无从定位。
|
||||
// 这里把它转发到 android.log → MiliLog,并保留原始 console 行为。
|
||||
// 诊断 + console 桥(必须在主脚本之前执行):
|
||||
// 1) 上报 WebView 内核版本与 ES2017(async/await) 支持情况 —— 老盒子(Android 5.x 的
|
||||
// WebView 可能是 Chrome 39~4x)不支持 async/await 会让**整个脚本语法报错**,
|
||||
// 表现为 `window.KtvApi` 未导出、桥永远不就绪;这条日志可直接定死该原因。
|
||||
// (用 new Function 做特性探测,避免探测语句本身引起语法错误。)
|
||||
// 2) window.onerror 捕获脚本解析/运行错误 —— 语法错误也会触发它,能拿到真实报错。
|
||||
// 3) console.log 桥接:ktv_api.js 的 _log() 细节原本只进 WebView 控制台。
|
||||
val consoleBridge = """
|
||||
(function(){
|
||||
try {
|
||||
window.onerror = function(msg, src, line, col){
|
||||
try { android.log('JS-ERROR ' + msg + ' @' + line + ':' + col); } catch(e) {}
|
||||
};
|
||||
} catch(e) {}
|
||||
try {
|
||||
var es = 'unknown';
|
||||
try { new Function('return (async function(){})'); es = 'yes'; } catch(e) { es = 'no'; }
|
||||
try {
|
||||
android.log('WEBVIEW ua=' + navigator.userAgent +
|
||||
' asyncFunction=' + es + ' promise=' + (typeof Promise));
|
||||
} catch(e) {}
|
||||
} catch(e) {}
|
||||
try {
|
||||
var orig = console.log;
|
||||
console.log = function(){
|
||||
|
||||
@@ -36,8 +36,15 @@ object KtvNetworkMonitor {
|
||||
private var manager: ConnectivityManager? = null
|
||||
private var callback: ConnectivityManager.NetworkCallback? = null
|
||||
|
||||
/** 开始监测;[onChange] 只在状态真正变化时回调(主线程)。 */
|
||||
fun start(context: Context, onChange: (online: Boolean) -> Unit) {
|
||||
/**
|
||||
* 开始监测;[onChange] 在**状态变化**时回调(主线程)。
|
||||
*
|
||||
* ⚠️ 第二参数 [initial] 区分「启动时的初始状态」与「真正的断网→恢复」:
|
||||
* 注册回调的当下系统就会回报一次初始状态,上层若把它当成"网络已恢复",
|
||||
* 就会在启动阶段做一遍**重复且有害**的恢复动作(重建歌曲源会话、复位窗口守卫、
|
||||
* 把状态栏写成"网络已恢复")。见 [MainActivity.onNetworkStateChanged]。
|
||||
*/
|
||||
fun start(context: Context, onChange: (online: Boolean, initial: Boolean) -> Unit) {
|
||||
if (callback != null) return
|
||||
val cm = context.applicationContext
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
|
||||
@@ -76,7 +83,7 @@ object KtvNetworkMonitor {
|
||||
manager = null
|
||||
}
|
||||
Log.i(TAG, "monitor started, online=$initial registered=$registered")
|
||||
onChange(initial)
|
||||
onChange(initial, true)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
@@ -88,17 +95,17 @@ object KtvNetworkMonitor {
|
||||
Log.i(TAG, "monitor stopped")
|
||||
}
|
||||
|
||||
private fun publish(value: Boolean, onChange: (Boolean) -> Unit) {
|
||||
private fun publish(value: Boolean, onChange: (Boolean, Boolean) -> Unit) {
|
||||
mainHandler.post {
|
||||
if (value == online) return@post
|
||||
online = value
|
||||
Log.i(TAG, "network state changed: online=$value")
|
||||
onChange(value)
|
||||
onChange(value, false)
|
||||
}
|
||||
}
|
||||
|
||||
/** 主动复检一次(例如从后台回到前台),有变化才回调。 */
|
||||
fun refresh(onChange: (online: Boolean) -> Unit) {
|
||||
fun refresh(onChange: (online: Boolean, initial: Boolean) -> Unit) {
|
||||
publish(currentOnline(manager), onChange)
|
||||
}
|
||||
|
||||
|
||||
@@ -531,7 +531,9 @@ class MainActivity : AppCompatActivity() {
|
||||
updateMobileQrOverlay()
|
||||
main.postDelayed(remoteNetworkMonitor, 5000)
|
||||
// 网络状态监测: 断网提示 + 恢复后重建歌曲源会话/重试下载。
|
||||
KtvNetworkMonitor.start(applicationContext) { online -> onNetworkStateChanged(online) }
|
||||
KtvNetworkMonitor.start(applicationContext) { online, initial ->
|
||||
onNetworkStateChanged(online, initial)
|
||||
}
|
||||
// 设备心跳:开机立即上报一次(boot=1),之后每 3600s 保活,维持歌曲源合法设备判定。
|
||||
sendDeviceHeartbeat(boot = true)
|
||||
main.postDelayed(ktvHeartbeatRunnable, HEARTBEAT_INTERVAL_MS)
|
||||
@@ -3684,13 +3686,24 @@ class MainActivity : AppCompatActivity() {
|
||||
* 4. 拉起本地点歌服务并刷新二维码;
|
||||
* 5. 自动重试断网期间失败的下载。
|
||||
*/
|
||||
private fun onNetworkStateChanged(online: Boolean) {
|
||||
private fun onNetworkStateChanged(online: Boolean, initial: Boolean = false) {
|
||||
fullScreenNetworkBadge?.visibility = if (online) View.GONE else View.VISIBLE
|
||||
if (!online) {
|
||||
status?.text = "网络未连接"
|
||||
MiliLog.warn("NET-DOWN 设备网络已断开")
|
||||
return
|
||||
}
|
||||
// ⚠️ initial=true 表示「启动时的初始状态」,**不是**「断网后恢复」。
|
||||
// 启动流程(onCreate)已经做过 startRemoteServer()/updateMobileQrOverlay()/
|
||||
// refreshLogDeviceContext()/DeviceMac 解析,这里再走一遍恢复逻辑只会:
|
||||
// 1) 白白清掉刚建立的歌曲源会话(resetSourceSession);
|
||||
// 2) 语义错误地 noteUrlOk(),可能掩盖真正的歌曲源窗口到期提示;
|
||||
// 3) 把状态栏错写成"网络已恢复"(启动瞬间就显示,用户可见的错误文案)。
|
||||
// 因此初始状态只更新断网角标,不做任何恢复动作。
|
||||
if (initial) {
|
||||
Log.i(TAG, "initial network state: online=true (skip recovery actions)")
|
||||
return
|
||||
}
|
||||
MiliLog.notice("NET-UP 设备网络已恢复, 重建歌曲源会话")
|
||||
if (!remoteServer.isRunning()) startRemoteServer()
|
||||
refreshNetworkState("net-up", notify = true)
|
||||
|
||||
@@ -29,8 +29,8 @@ object SongApiClient {
|
||||
SongUrlResolver.install { url -> bridge?.rawCleanGet(url) }
|
||||
bridge?.init { ok ->
|
||||
ready = ok
|
||||
// 失败原因由 KtvJsBridge.markUnavailable() 统一上报(只记一次),此处不再重复记日志。
|
||||
Log.i(TAG, "JS Bridge init: $ok")
|
||||
if (!ok) MiliLog.error("JS-BRIDGE 初始化返回 false,取址将走原生兜底")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user