From 808b9c40d941c8901dd64f3d41ccd0ebf65f36ab Mon Sep 17 00:00:00 2001 From: sky121113 Date: Mon, 1 Jun 2026 17:48:32 +0800 Subject: [PATCH] =?UTF-8?q?[FIX]=20=E4=BF=AE=E6=AD=A3=20OTA=20=E9=80=A3?= =?UTF-8?q?=E7=B7=9A=E8=B6=85=E6=99=82=E8=88=87=E9=A6=96=E9=A0=81=E5=9F=B7?= =?UTF-8?q?=E8=A1=8C=E7=B7=92=E4=BD=B5=E7=99=BC=E7=AB=B6=E6=85=8B=E5=B0=8E?= =?UTF-8?q?=E8=87=B4=20UI=20=E7=95=B0=E5=B8=B8=E5=95=8F=E9=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. OtaManager.java: 將下載的連線逾時從 15 秒調高至 30 秒,讀取逾時調高至 120 秒,提升 OTA 下載穩定性。 2. FontendActivity.java: 修正多執行緒更新 UI 時,共用 logs 緩衝區所引發的競態條件 (Race Condition) 導致欄位變空的問題,改採區域變數更新;並安全封裝重構 reportOnScheduler 的生命週期管理方法;調整閒置逾時為 5 分鐘。 3. build.gradle: 依據實體機台 OTA 版號自動化規範,安全遞增 verPatch 修補版號至 56。 4. compiler-apk.md: 優化編譯工作流中的後台更新說明提示欄位。 --- .agents/workflows/compiler-apk.md | 1 + app/build.gradle | 2 +- .../unibuy/smartdevice/tools/OtaManager.java | 6 +- .../smartdevice/ui/FontendActivity.java | 67 ++++++++++--------- 4 files changed, 43 insertions(+), 33 deletions(-) diff --git a/.agents/workflows/compiler-apk.md b/.agents/workflows/compiler-apk.md index 08cd42d..a2242dd 100644 --- a/.agents/workflows/compiler-apk.md +++ b/.agents/workflows/compiler-apk.md @@ -157,6 +157,7 @@ if [ -n "$APK_SOURCE" ]; then echo "* 版本名稱 (versionName):$VERSION_NAME" echo "* 版本代碼 (versionCode):$VERSION_CODE" echo "* APK 檔案名稱:$APK_NAME" + echo "* 更新說明:[請在此處附上口語化的一句話更新說明]" echo "==================================================" else echo "❌ 找不到編譯產出的 APK 檔案,複製失敗!" diff --git a/app/build.gradle b/app/build.gradle index c0a409a..a85360a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ plugins { // 避免不同尺寸機台間版本號產生衝突。 // ========================================================================= def verMinor = 1 -def verPatch = 38 +def verPatch = 56 android { diff --git a/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java b/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java index f40d825..ed1542f 100644 --- a/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java +++ b/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java @@ -17,6 +17,8 @@ import java.net.URL; public class OtaManager { private static final String TAG = "OtaManager"; private static final Logs logs = new Logs(OtaManager.class); + private static final int DOWNLOAD_CONNECT_TIMEOUT_MS = 30000; + private static final int DOWNLOAD_READ_TIMEOUT_MS = 120000; /** * 啟動背景執行緒執行 APP OTA 更新 @@ -67,8 +69,8 @@ public class OtaManager { private static File downloadApk(Context context, String apkUrl) throws Exception { URL url = new URL(apkUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setConnectTimeout(15000); - connection.setReadTimeout(15000); + connection.setConnectTimeout(DOWNLOAD_CONNECT_TIMEOUT_MS); + connection.setReadTimeout(DOWNLOAD_READ_TIMEOUT_MS); connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java index bfa0bd8..1b46301 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java @@ -118,11 +118,11 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl private Fragment activeFragment; private Fragment vmcFragment; private Fragment electricFragment; - private static ReportShotTimeByScheduler reportOnScheduler; + private ReportShotTimeByScheduler reportOnScheduler; private ConnectivityManager connectivityManager; private ConnectivityManager.NetworkCallback networkCallback; - private static final long IDLE_TIMEOUT = 5 * 60 * 1000; // 3 分鐘 + private static final long IDLE_TIMEOUT = 5 * 60 * 1000; // 5 分鐘 private final long TIME_LIMIT = 200000; private int clickCount = 0; private long firstClickTime = 0; @@ -263,7 +263,8 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl // 【第一層】定時重啟:到了整點就啟動等待模式(RestartAPP2 自己會等閒置 >= 90 秒才真重啟) if (timeByM.equals("04:00") || timeByM.equals("08:00") || timeByM.equals("12:00") || timeByM.equals("16:00") || timeByM.equals("20:00") || timeByM.equals("23:59")) { - android.content.SharedPreferences prefs = getSharedPreferences("AppRestartPrefs", Context.MODE_PRIVATE); + android.content.SharedPreferences prefs = getSharedPreferences("AppRestartPrefs", + Context.MODE_PRIVATE); String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date()); String currentSlot = today + " " + timeByM; String lastRestartSlot = prefs.getString("last_restart_slot", ""); @@ -280,11 +281,11 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl // 【第二層】閒置深度刷新:任何時間閒置超過 30 分鐘就重啟 (暫時關閉,改依賴定時重啟) /* - if (MyApp.getInstance().getIdleSeconds() >= 30 * 60) { - getLogs().info("閒置超過 30 分鐘,執行深度排毒重啟..."); - new RestartAPP2(this).start(); - } - */ + * if (MyApp.getInstance().getIdleSeconds() >= 30 * 60) { + * getLogs().info("閒置超過 30 分鐘,執行深度排毒重啟..."); + * new RestartAPP2(this).start(); + * } + */ // 這裡判斷每5分鐘去跑連線下位機的狀態 // String minutePart = timeByM.split(":")[1]; // 取得 "mm" 部分 @@ -305,24 +306,24 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl // new CMUHAutoRun(this).start(); } - getLogs().update("ThreadCount", String.valueOf(Tools.threadCount())); - String tc = getLogs().getMessage(); - getLogs().update("ViceoCount", String.valueOf(VideoEoxPlayer.getExoPlayerTotal())); - String vc = getLogs().getMessage(); - binding.textThreadCount.setText("TC:" + tc + " VC:" + vc); - getLogs().clearMessage(); + // 直接使用區域變數更新 UI,避免多執行緒 Race Condition 導致 getLogs().getMessage() 被改寫或清空而使 UI 變空 + String threadCountStr = String.valueOf(Tools.threadCount()); + String videoCountStr = String.valueOf(VideoEoxPlayer.getExoPlayerTotal()); + binding.textThreadCount.setText("TC:" + threadCountStr + " VC:" + videoCountStr); + + getLogs().update("ThreadCount", threadCountStr); + getLogs().update("ViceoCount", videoCountStr); Tools.threadInfos(); String networkDevice = getNetworkDevice(); + binding.textNetwork.setText(getString(R.string.network) + ":" + networkDevice); getLogs().update("Network", networkDevice); - binding.textNetwork.setText(getString(R.string.network) + ":" + getLogs().getMessage()); - getLogs().clearMessage(); DevXinYuanController.setVersion(ver); + String tempStr = DevXinYuanController.getTemperature() + "℃"; + binding.textTemperature.setText(getString(R.string.temp) + ":" + tempStr); getLogs().update("Temperature", String.valueOf(DevXinYuanController.getTemperature()), "℃"); - binding.textTemperature.setText(getString(R.string.temp) + ":" + getLogs().getMessage()); - getLogs().clearMessage(); try { // httpAPI.detectHost(); @@ -467,7 +468,6 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl return saleFlowHandler; } - public void startOnSwitchFragment() { try { stopOnSwitchFragment(); // ✅ 先安全釋放舊播放器 @@ -487,7 +487,8 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl videoEoxPlayer = new VideoEoxPlayer(getCtx()); // ✅ 用新版建構子 getHandlerMain().getHandler().postDelayed(() -> { - if (isFinishing() || isDestroyed()) return; + if (isFinishing() || isDestroyed()) + return; if (videoEoxPlayer != null) { // videoEoxPlayer.playMedia(binding.videoView, binding.imageAd); // ✅ 傳入 view // 開始播放 @@ -700,12 +701,7 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl binding.floatButtonShoppingCart.setVisibility(View.GONE); } - if (reportOnScheduler != null) { - reportOnScheduler.shutdown(); - } - - reportOnScheduler = new ReportShotTimeByScheduler(getHandlerMain()); - reportOnScheduler.start(1L, 10L, TimeUnit.SECONDS); + startHeaderReportScheduler(); // binding.btnswitchlanTW.setOnClickListener(v -> switchLanguage("tw")); // @@ -753,6 +749,20 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl startOnSwitchFragment(); } + private void startHeaderReportScheduler() { + stopHeaderReportScheduler(); + reportOnScheduler = new ReportShotTimeByScheduler(getHandlerMain()); + getHandlerMain().start(getClass().getSimpleName(), Option.REPORT_AND_SHOT_TIME.getOption(), "start"); + reportOnScheduler.start(1L, 10L, TimeUnit.SECONDS); + } + + private void stopHeaderReportScheduler() { + if (reportOnScheduler != null) { + reportOnScheduler.shutdown(); + reportOnScheduler = null; + } + } + private void hiddenMultiLanguageButton() { binding.btnswitchlanTW.setVisibility(View.GONE); binding.btnswitchlanEn.setVisibility(View.GONE); @@ -1230,9 +1240,6 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl } } - if (reportOnScheduler != null) { - reportOnScheduler.shutdown(); - reportOnScheduler = null; - } + stopHeaderReportScheduler(); } }