emitter 增强 多条件触发

本文深入探讨了事件驱动编程模型的实现方式,通过定义事件监听器、触发事件和管理监听者,展示了如何创建一个轻量级且高效的消息传递系统。文章详细解释了事件注册、事件触发、事件监听的一次性和多次性,以及如何处理事件冒泡等问题。
 1 ;(function(global ,undefined){
 2     var evts = {}
 3         ,onceTag = '__event_once'
 4     function emit(event ){
 5         var args = util.toArray(arguments , 1)
 6         if (!(event in evts)) return
 7         var _dels = []
 8         for (var i = 0 , j = evts[event].length ; i < j ;i ++){
 9             var cbk = evts[event][i]
10             if (!cbk) return
11             cbk.apply(null , args)
12             if (cbk[onceTag])  { evts[event][i] = null ; _dels.push[i]}
13         }
14         for (var i = _dels.length -1 ; i>=0 ; i--) evts[event].splice(_dels[i] , 1)
15     }
16 
17     function addMultiCon(event , listener){
18         var once = true
19         event.sort()
20         addListener(event.join('|') , listener , once)
21         var eventBubbles = []
22         function tinOpener(evt){
23             eventBubbles.push(evt)
24             if (eventBubbles.length >=  event.length) {
25                 eventBubbles.sort()
26                 emit(eventBubbles.join('|'))
27             }
28         }
29 
30         for (var i = 0 ; i < event.length;i ++){
31             addListener(event[i] , tinOpener.bind(null, event[i]) , once)
32         }
33     }
34 
35     function addListener(event , listener , once){
36         if (util.isArray(event)) return addMultiCon(event , listener)
37 
38 
39         if (!(event in evts)) evts[event] = []
40         if (once) listener[onceTag] = true
41         evts[event].push(listener)
42     }
43 
44     function removeListener(event, listener){
45         if (!listener) {
46             delete evts[event]
47             return
48         }
49         for (var i = 0 , j = evts[event].length ; i < j ;i ++){
50             if (evts[event][i] === listener) {  evts[event].splice(i, 1) ; break}
51         }
52     }
53 
54     function listeners(event){
55         return evts[event]
56     }
57     global.emitter = {
58         on : addListener
59         ,once : function(event , listener){
60                 addListener(event , listener , true)
61                 }
62         ,emit : emit
63         ,listeners : listeners
64     }
65 })(this)
 function toArray(colletions ,offset){
    return Array.prototype.slice.call(colletions , offset || 0)
}

 

 

 

emitter.on(['test','test2'] , function(){
   console.log('test is fired')
})

emitter.emit('test')

emitter.emit('test2')

转载于:https://www.cnblogs.com/vaal-water/p/3949359.html

package fn.server.controller; import fn.server.message.MessageStorageManager; import fn.server.message.SseConnectionManager; import fn.server.message.SseMessageService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @RestController @Slf4j @RequestMapping("/sse") public class SseController { @Autowired private SseConnectionManager connectionManager; @Autowired private MessageStorageManager storageManager; @Autowired private SseMessageService messageService; /** * 用户建立SSE连接 */ @GetMapping(value = "/connect/{userId}") public SseEmitter connect( @PathVariable String userId, @RequestParam(required = false) String companyId, HttpServletResponse response) { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type", "text/event-stream;charset=UTF-8"); SseEmitter emitter = new SseEmitter(TimeUnit.MINUTES.toMillis(60)); // 60分钟超时 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); // 每30秒发送一次心跳 ScheduledFuture<?> heartbeatFuture = executor.scheduleAtFixedRate(() -> { try { emitter.send(SseEmitter.event().comment("heartbeat")); } catch (IOException e) { log.warn("Heartbeat failed for user {}: {}", userId, e.getMessage()); } }, 0, 30, TimeUnit.SECONDS); // 绑定用户连接 connectionManager.addEmitter(userId, companyId, emitter); // 发送初始数据 try { storageManager.sendPendingMessages(userId, messageService); emitter.send("中文信息".getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { log.error("Initial data send failed for user {}: {}", userId, e.getMessage()); } // 连接完成或超时处理 emitter.onCompletion(() -> { heartbeatFuture.cancel(true); executor.shutdown(); connectionManager.removeEmitter(userId); }); emitter.onTimeout(() -> { emitter.complete(); connectionManager.removeEmitter(userId); }); return emitter; } @GetMapping("/logout/{userId}") public String logout(@PathVariable String userId) { try { // 断开用户的SSE连接 connectionManager.removeEmitter(userId); return "User " + userId + " logged out and connection closed."; } catch (Exception e) { e.printStackTrace(); return "Error while logging out user " + userId; } } } 这段代码老是报错2025-03-10 10:50:51.114 WARN 9328 --- [ol-102-thread-1] fn.server.controller.SseController : Heartbeat failed for user e82c24aa-d619-4754-8d56-ef58d48c028d: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
03-11
package com.kotei.overseas.navi.update; import static com.kotei.overseas.navi.security.DecryptUtil.dataVerification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; import android.util.Log; import androidx.annotation.NonNull; import com.here.sdk.core.engine.SDKNativeEngine; import com.here.sdk.maploader.MapDownloader; import com.here.sdk.maploader.MapDownloaderConstructionCallback; import com.kotei.overseas.navi.business.data.MapDataController; import com.kotei.overseas.navi.security.DecryptUtil; import com.kotei.overseas.navi.security.DfCert; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import java.util.stream.Stream; //rxjava import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.ObservableEmitter; import io.reactivex.rxjava3.core.ObservableOnSubscribe; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.subjects.PublishSubject; /** * USB离线更新系统 */ public class USBOfflineUpdater { private static final String TAG = "USBOfflineUpdater"; // 状态码定义 /** * 操作成功 */ public static final int SUCCESS = 0; /** * 错误:未检测到USB设备 */ public static final int ERROR_NO_USB = 1; /** * 错误:未找到升级包文件 */ public static final int ERROR_NO_UPDATE_PACKAGE = 2; /** * 错误:电池电量不足(低于安全阈值) */ public static final int ERROR_BATTERY_LOW = 3; /** * 错误:存储空间不足 */ public static final int ERROR_STORAGE_INSUFFICIENT = 4; /** * 错误:系统正在执行其他升级任务 */ public static final int ERROR_UPDATE_IN_PROGRESS = 5; /** * 错误:文件复制失败(检查存储权限或磁盘状态) */ public static final int ERROR_COPY_FAILED = 6; /** * 错误:升级包解压失败(文件可能损坏) */ public static final int ERROR_EXTRACT_FAILED = 7; /** * 错误:用户手动取消操作 */ public static final int ERROR_USER_CANCELED = 8; /** * 错误:未预期的系统异常 */ public static final int ERROR_UNEXPECTED = 9; /** * 错误:升级过程中USB设备被移除 */ public static final int ERROR_USB_REMOVED = 10; /** * 错误:车辆档位未处于停车挡(P档) */ public static final int ERROR_VEHICLE_SHIFTED = 11; /** * 错误:电池电量极低(无法维持升级过程) */ public static final int ERROR_BATTERY_TOO_LOW = 12; /** * 错误:文件校验失败(MD5/SHA256校验不匹配) */ public static final int ERROR_FILE_VERIFY_FAILED = 13; /** * 错误:文件解密或验签失败 */ public static final int ERROR_DECRYPT_OR_SIGN_FAILED = 14; // 更新阶段定义 /** * 空闲状态(未开始升级) */ private static final int PHASE_IDLE = 0; /** * 设备检测阶段(检查USB/存储设备) */ private static final int PHASE_DETECTING = 1; /** * 升级包校验阶段(验证完整性/签名) */ private static final int PHASE_CHECKING = 2; /** * 系统备份阶段(备份当前系统数据) */ private static final int PHASE_BACKUP = 3; /** * 文件复制阶段(写入升级包到临时分区) */ private static final int PHASE_COPYING = 4; /** * 解压阶段(解压升级包内容) */ private static final int PHASE_EXTRACTING = 5; /** * 清理阶段(删除临时文件) */ private static final int PHASE_CLEANUP = 6; /** * 回滚阶段(升级失败时恢复备份) */ private static final int PHASE_ROLLBACK = 7; public static final int ERROR_PHASE_ROLLBACK = 101; // 权重分配比例 private static final float BACKUP_WEIGHT = 0.1f; // 备份阶段权重10% private static final float PACKAGE_COPY_WEIGHT = 0.29f; // 升级包拷贝阶段权重29% private static final float PACKAGE_VERIFY_WEIGHT = 0.31f; // 升级包解密验签阶段权重31% private static final float PACKAGE_EXTRACT_WEIGHT = 0.29f; // 升级包解压阶段权重29% private static final float VERIFICATION_WEIGHT = 0.01f; // 校验阶段权重1% // 声明 mProgress 为实例变量(非静态) private float mProgress = 0; // 当前进度值(0~1) private static USBOfflineUpdater instance; private final Context context; // private UpdateTask currentTask; 0903 private RxUpdateManager rxUpdateManager; // private UpdateListener updateListener; private SDKNativeEngine sdkNativeEngine; private MapDataController mapDataController; // 目录配置 private File usbRoot; private File cacheDir; private File storageDir;// private File backupDir; // private File backupAlreadyDir; // 更新控制 public boolean isPaused = false; public boolean isCancelled = false; public final AtomicInteger currentPhase = new AtomicInteger(PHASE_IDLE); // 错误信息 private String lastErrorMessage = ""; // 电量阈值 private static final int MIN_BATTERY_LEVEL = 30; // 最低电量百分比 private static final int MIN_BATTERY_LEVEL_CRITICAL = 15; // 严重低电量 //升级包格式 private static final String FILE_NAME_PATTERN = "^KVM_Navi_EU_" + "(?<version>\\d{1,3})" // 版本号(1-3位数字) + "_" + "(?<serial>\\d{1,2})" // 1-2位数字编号 + "(\\.\\w+)?$"; // 可选的文件扩展名 // 进度计算相关变量(新增) private long totalUpdateSize = 0; // 所有升级包总大小 private long UpdateSize = 0; // 当前升级包大小 private long currentCopiedBytes = 0; // 当前已拷贝字节数 private long currentVerifiedBytes = 0; // 当前已验签字节数 private long currentExtractedBytes = 0; // 当前已解压字节数 private long backupSize = 0; // 备份数据大小 private long storageSize = 0; private int currentPackageIndex = 0; // 当前处理的升级包索引 private int totalPackageCount = 0; // 总升级包数量 // 用于跟踪回滚状态 public boolean isRollingBack = false; public long rollbackTotalSize = 0; public long rollbackProcessedSize = 0; public boolean ishandleFailure = false; private final Object updateLock = new Object(); // 用于更新与回滚的互斥 // USB监听器 private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) { //zwxtest // File usbPath = new File(intent.getData().getPath()); File usbPath = new File("/data/data/com.kotei.overseas.navi/files"); if (usbPath.exists() && usbPath.canRead()) { usbRoot = usbPath; Log.i(TAG, "USB mounted: " + usbRoot.getAbsolutePath()); } } else if (Intent.ACTION_MEDIA_EJECT.equals(action) || Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) { // 0903 -省略 if (currentTask != null && currentPhase.get() > PHASE_CHECKING) { // cancelUpdate(ERROR_USB_REMOVED, "USB设备被移除"); // } usbRoot = null; Log.e(TAG, "USB removed"); } } }; // // 车辆状态监听器(模拟) private final BroadcastReceiver vehicleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if ("com.example.ACTION_SHIFT_CHANGE".equals(intent.getAction())) { String shift = intent.getStringExtra("shift"); if (!"P".equals(shift) && currentPhase.get() > PHASE_CHECKING) { // cancelUpdate(ERROR_VEHICLE_SHIFTED, "车辆已退出P挡"); } } } }; // 单例模式 public static synchronized USBOfflineUpdater getInstance(Context context) { if (instance == null) { instance = new USBOfflineUpdater(context); } return instance; } public static synchronized USBOfflineUpdater getInstance() { return instance; } private USBOfflineUpdater(Context context) { this.context = context.getApplicationContext(); // 初始化SDK sdkNativeEngine = SDKNativeEngine.getSharedInstance(); mapDataController = MapDataController.getInstance(); try { DfCert.getInstance().getService(); } catch (Exception e) { Log.e(TAG, "Exception:" + e.toString()); } //zwx 执行顺序? // 初始化目录(默认值) Log.i(TAG, "zwx:=== 启动目录初始化 ===" ); cacheDir = this.context.getCacheDir(); storageDir = new File(sdkNativeEngine.getOptions().persistentMapStoragePath); // ✅ 初始化 backupDir backupDir = new File(cacheDir, "backup"); // ✅ 基于 cacheDir 固定路径 backupAlreadyDir = new File(cacheDir, "backupAlready"); // 注册USB监听器 IntentFilter usbFilter = new IntentFilter(); usbFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); usbFilter.addAction(Intent.ACTION_MEDIA_EJECT); usbFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); usbFilter.addDataScheme("file"); context.registerReceiver(usbReceiver, usbFilter); // 注册车辆状态监听器(模拟) IntentFilter vehicleFilter = new IntentFilter("com.example.ACTION_SHIFT_CHANGE"); context.registerReceiver(vehicleReceiver, vehicleFilter); //清除数据存储目录下的预留数据 removeLegacy(); } public void initialization(UpdateListener listener) { isRollingBack = false; this.updateListener = listener; Thread USBOfflineUpdaterInitialization = new Thread(new Runnable() { @Override public void run() { // 启动时检查恢复 checkRecoveryOnStartup(); } }); USBOfflineUpdaterInitialization.setName("USBOfflineUpdaterInitialization"); USBOfflineUpdaterInitialization.start(); } // 动态设置目录 public void setDirectories(File usbRoot, File cacheDir, File storageDir) { if (usbRoot != null) { this.usbRoot = usbRoot; } if (cacheDir != null) { this.cacheDir = cacheDir; } if (storageDir != null) { this.storageDir = storageDir; } } /** * 检测升级包 * * @return 状态码 (SUCCESS 或错误码) */ public int detectUpdatePackages() { //zwxtest // 1. 检测USB是否插入 File usbPath = new File("/data/data/com.kotei.overseas.navi/files"); if (usbPath.exists() && usbPath.canRead()) { usbRoot = usbPath; Log.i(TAG, "USB mounted: " + usbRoot.getAbsolutePath()); } //zwxtest if (usbRoot == null || !usbRoot.exists() || !usbRoot.isDirectory()) { return ERROR_NO_USB; } File[] tempPackages = usbRoot.listFiles(); // 2. 查找升级包 (命名格式: update_v{版本号}_{日期}.zip) File[] packages = usbRoot.listFiles(file -> file.isFile() && file.getName().matches(FILE_NAME_PATTERN) ); return (packages != null && packages.length > 0) ? SUCCESS : ERROR_NO_UPDATE_PACKAGE; } /** * 环境检测 * * @return 状态码 (SUCCESS 或错误码) */ public int checkEnvironment() { // 1. 检测电量 int batteryLevel = PowerUtils.getBatteryLevel(context); if (batteryLevel < MIN_BATTERY_LEVEL) { return batteryLevel < MIN_BATTERY_LEVEL_CRITICAL ? ERROR_BATTERY_TOO_LOW : ERROR_BATTERY_LOW; } // 2. 检测缓存空间 (需大于15GB) long requiredSpace = 15L * 1024 * 1024 * 1024; // 15GB long availableSpace = StorageUtils.getAvailableSpace(cacheDir); if (availableSpace < requiredSpace) { Log.e(TAG, "缓存空间剩余:【" + availableSpace + "】"); return ERROR_STORAGE_INSUFFICIENT; } return SUCCESS; } /** * 判读是否正在进行离线更新 */ public boolean isOfflineUpdate() { return rxUpdateManager!= null && !isCancelled; // 0903-待修改 return currentTask != null && !currentTask.isCancelled(); } /** * 开始更新 */ public void startUpdate(UpdateListener listener) { Log.i(TAG,"检查是否处于回滚状态:"+ishandleFailure); this.updateListener = listener; if (ishandleFailure && backupAlreadyDir.exists()) { new Handler(Looper.getMainLooper()).post(() -> { if (updateListener != null) { // 显示回滚阶段和进度 float progress = (float) (rollbackProcessedSize * 100) / rollbackTotalSize; progress = Math.round(progress * 100) / 100.0f; // 保留两位小数 updateListener.onProgress(PHASE_ROLLBACK, progress, "正在回滚数据..."); } }); return; } int result = checkEnvironment(); if (result != SUCCESS) { notifyListener(result, "环境检测不合格"); return; } if (isRollingBack) { notifyListener(ERROR_UPDATE_IN_PROGRESS, "正在进行数据回滚"); return; } if (isOfflineUpdate()) { notifyListener(ERROR_UPDATE_IN_PROGRESS, "已有更新任务正在进行"); return; } Log.i(TAG, "检测到更新任务触发,开始进行地图更新"); // 计算总工作量(新增) calculateTotalWorkload();//p3-2 //0903已经修改 // notifyProgress("开始进行更新"); rxUpdateManager = new RxUpdateManager(); rxUpdateManager.startUpdate(); } /** * 开始更新 */ // 计算总工作量(新增) private void calculateTotalWorkload() { totalUpdateSize = 0; File[] packages = getUpdatePackages(); totalPackageCount = packages != null ? packages.length : 0; if (packages != null) { for (File pkg : packages) { totalUpdateSize += pkg.length(); } } // backupSize = estimateBackupSize();//zwx backupSize = FileUtils.getDirectorySize(storageDir); Log.i(TAG, "总工作量计算: 升级包数量=" + totalPackageCount + ", 升级包大小=" + formatSize(totalUpdateSize) + ", 备份大小=" + formatSize(backupSize)); } // 获取更新包(新增) private File[] getUpdatePackages() { if (usbRoot == null) return new File[0]; return usbRoot.listFiles(file -> file.isFile() && file.getName().matches(FILE_NAME_PATTERN) ); } // 格式化文件大小(新增) private String formatSize(long size) { if (size < 1024) return size + "B"; else if (size < 1024 * 1024) return String.format("%.1fKB", size / 1024.0); else if (size < 1024 * 1024 * 1024) return String.format("%.1fMB", size / (1024.0 * 1024)); else return String.format("%.1fGB", size / (1024.0 * 1024 * 1024)); } /** * 暂停更新 */ public void pauseUpdate() { isPaused = true; notifyProgress("更新已暂停"); } /** * 恢复更新 */ public void resumeUpdate() { isPaused = false; notifyProgress("更新已恢复"); } /** * 取消更新 */ public void cancelUpdate() { cancelUpdate(ERROR_USER_CANCELED, "用户取消更新"); } public void cancelUpdate(int errorCode, String message) { Log.i(TAG,"触发取消按钮"); isCancelled = true; lastErrorMessage = message; //zwx-focus-p3-回滚触发了计算总进度 // if (currentTask != null) { // currentTask.cancel(true); // 请求中断线程 // // } //p6 notifyListener(errorCode, message);//p7 rxUpdateManager.cancelrxUpdateManager(); } //p3-4-回滚使用自定义进度计算 private void notifyProgress(String message) { new Handler(Looper.getMainLooper()).post(() -> { if (updateListener != null) { float progress; if (isRollingBack) { Log.i(TAG,"进入回滚对应进度计算"); float progress_orin = (float) (rollbackProcessedSize * 100) / rollbackTotalSize; progress = Math.round(progress_orin * 100) / 100.0f; } else { Log.i(TAG,"进入普通进度计算:对应环节"+ currentPhase.get()); progress = calculateOverallProgress(); } updateListener.onProgress(currentPhase.get(), progress, message); } }); } private float calculateOverallProgress() { // if (isRollingBack) { // // 回滚阶段:直接计算回滚进度 // if (rollbackTotalSize > 0) { // mProgress = 99; // return mProgress; // } // return 0; // } if (totalPackageCount == 0 && currentPhase.get() != PHASE_ROLLBACK) { throw new IllegalStateException("totalPackageCount should not be 0 here!"); }//p3-3 if (totalPackageCount == 0 && currentPhase.get() != PHASE_ROLLBACK) return 0; // 每个包的总权重(拷贝+验签+解压) float packageTotalWeight = PACKAGE_COPY_WEIGHT + PACKAGE_VERIFY_WEIGHT + PACKAGE_EXTRACT_WEIGHT; //再次确认totalPackageCount是否等于0 if (totalPackageCount == 0) { throw new IllegalStateException("totalPackageCount should not be 0 here!"); } // 每个包的阶段权重 float packageCopyWeight = PACKAGE_COPY_WEIGHT / totalPackageCount; float packageVerifyWeight = PACKAGE_VERIFY_WEIGHT / totalPackageCount; float packageExtractWeight = PACKAGE_EXTRACT_WEIGHT / totalPackageCount; switch (currentPhase.get()) { case PHASE_BACKUP: if(backupSize > 0) { mProgress = BACKUP_WEIGHT * (currentCopiedBytes / (float) backupSize); }else{ mProgress = BACKUP_WEIGHT; } if(mProgress > BACKUP_WEIGHT) { mProgress = BACKUP_WEIGHT; } break; case PHASE_COPYING: // 基础:备份 + 已完成包的完整进度 float copyBase = BACKUP_WEIGHT + (packageTotalWeight * currentPackageIndex) / totalPackageCount; // 增量:当前包拷贝进度 float copyProgress = currentCopiedBytes / (float) UpdateSize; mProgress = copyBase + packageCopyWeight * copyProgress; break; case PHASE_CHECKING: // 基础:备份 + 已完成包的完整进度 + 当前包拷贝完成 float verifyBase = BACKUP_WEIGHT + (packageTotalWeight * currentPackageIndex) / totalPackageCount + packageCopyWeight; // 增量:当前包验签进度 float verifyProgress = currentVerifiedBytes / (float) UpdateSize; mProgress = verifyBase + packageVerifyWeight * verifyProgress; break; case PHASE_EXTRACTING: // 修复:添加当前包验签完成 float extractBase = BACKUP_WEIGHT + (packageTotalWeight * currentPackageIndex) / totalPackageCount + packageCopyWeight + packageVerifyWeight; // 添加这行 // 增量:当前包解压进度 float extractProgress = currentExtractedBytes / (float) UpdateSize; mProgress = extractBase + packageExtractWeight * extractProgress; break; case PHASE_DETECTING: mProgress = BACKUP_WEIGHT + packageTotalWeight + VERIFICATION_WEIGHT * (currentVerifiedBytes / (float) totalUpdateSize); break; case PHASE_CLEANUP: // case PHASE_ROLLBACK: // mProgress = 0.99f; // p3-5 break; } return Math.min(Math.round(mProgress * 10000) / 100.00f, 100.00f); } // 结果通知 private void notifyListener(int resultCode, String message) { new Handler(Looper.getMainLooper()).post(() -> { if (updateListener != null) { updateListener.onResult(resultCode, message); } }); } // 获取当前进度百分比 private int getCurrentProgress() { // 此处可添加子任务进度计算 return (int) mProgress; } //rxjavazwx---异步任务---zwx private class RxUpdateManager { private File[] updatePackages; private long updateSize = 0; private CompositeDisposable disposables = new CompositeDisposable(); // private PublishSubject<String> progressSubject = PublishSubject.create(); // private PublishSubject<Integer> resultSubject = PublishSubject.create(); public void startUpdate() { // 重置状态(与AsyncTask的onPreExecute一致) currentPhase.set(PHASE_DETECTING); isCancelled = false; isPaused = false; currentCopiedBytes = 0; currentVerifiedBytes = 0; currentExtractedBytes = 0; mProgress = 0; Observable<Integer> updateObservable = Observable.create(emitter -> { try { // 阶段1: 备份数据(对应PHASE_BACKUP) currentPhase.set(PHASE_BACKUP); if (emitter.isDisposed()) return; notifyProgress("开始备份数据..."); backupDir = new File(cacheDir, "backup"); if (backupDir.exists() && !FileUtils.deleteRecursive(backupDir)) { emitter.onError(new IOException("删除备份文件失败")); return; } if (!backupDir.mkdirs()) { emitter.onError(new IOException("创建备份目录失败")); return; } // backupSize = FileUtils.getDirectorySize(storageDir); Log.i(TAG,"storageDir大小:"+backupSize); if (backupSize > 0) { // 添加暂停/取消检查点 checkInterrupted(emitter); singlecopydirectory.copyDirectoryWithProgress(storageDir, backupDir, (copied, total) -> { // checkCancelled(emitter); // 新增中断检查 currentCopiedBytes = copied; notifyProgress("备份中: " + formatSize(copied) + "/" + formatSize(total)); } ); // 重命名备份目录(与AsyncTask一致) if (!backupDir.renameTo(backupAlreadyDir)) { emitter.onError(new IOException("重命名备份目录失败")); return; } } else { // 无数据需要备份,直接标记完成 Log.i(TAG, "无备份数据,直接进行数据更新"); currentCopiedBytes = 1; backupSize = 1; notifyProgress("无数据需要备份"); } // currentCopiedBytes = backupSize; // 阶段2: 处理升级包(对应PHASE_COPYING/CHECKING/EXTRACTING) currentCopiedBytes = 0; updatePackages = getUpdatePackages(); notifyProgress("开始处理升级包..."); Log.i(TAG, "开始处理升级包【拷贝、解密验签、解压】"); for (currentPackageIndex = 0; currentPackageIndex < updatePackages.length; currentPackageIndex++) { // 关键检查点:暂停/取消 checkInterrupted(emitter); File packageFile = updatePackages[currentPackageIndex]; updateSize = packageFile.length(); String packageName = packageFile.getName(); long packageSize = packageFile.length(); currentCopiedBytes = 0; currentPhase.set(PHASE_COPYING); // 子阶段1: 拷贝升级包 File destFile = new File(storageDir, packageName); boolean copyResult = FileUtils.copyFileWithProgress( packageFile, destFile, (copied, total) -> { currentCopiedBytes = copied; notifyProgress(String.format("拷贝 %s: %s/%s", packageName, formatSize(copied), formatSize(total))); } ); if (!copyResult || emitter.isDisposed()) { emitter.onError(new IOException("拷贝失败或被取消")); return; } currentVerifiedBytes = 0; currentPhase.set(PHASE_CHECKING); // 子阶段2: 解密验签(添加暂停检查点) checkInterrupted(emitter); DecryptUtil.ProgressCallback decryptCallback = new DecryptUtil.ProgressCallback() { @Override public void onProgress(long processed, long total) { // 直接更新验签进度计数器 currentVerifiedBytes = processed; // 触发进度通知 notifyProgress(String.format("解密验签 %s: %s/%s", packageName, formatSize(processed), formatSize(total))); } }; if (!dataVerification(destFile.getAbsolutePath(), decryptCallback)) { emitter.onError(new IOException("解密失败")); } currentVerifiedBytes = UpdateSize; currentPhase.set(PHASE_EXTRACTING); // 子阶段3: 解压(修复:重置解压计数器) checkInterrupted(emitter); currentExtractedBytes = 0; // 重置解压进度(与AsyncTask一致) notifyProgress("开始解压: " + packageName); boolean extractResult = FileUtils.extractZipWithProgress( destFile, storageDir, (extracted, total) -> { currentExtractedBytes = extracted; notifyProgress(String.format("解压 %s: %s/%s", packageName, formatSize(extracted), formatSize(total))); } ); if (!extractResult) { emitter.onError(new IOException("解压失败")); return; } // 删除已处理的升级包 if (!destFile.delete()) { System.out.println("删除升级包失败: " + packageName); } } // 阶段3: 校验结果(对应AsyncTask的校验步骤) if (!mapDataController.checkInstallationStatus()) { emitter.onError(new IOException("安装校验失败")); return; } //MapDownloader.fromEngineAsync(sdkNativeEngine, new MapDownloaderConstructionCallback() { // @Override // public void onMapDownloaderConstructedCompleted(@NonNull MapDownloader downloader) { // Log.i(TAG, "数据同步成功"); // } // }); currentPhase.set(PHASE_CLEANUP); // 阶段4: 清理(对应PHASE_CLEANUP) checkInterrupted(emitter); notifyProgress("清理缓存..."); if (backupDir.exists() && !FileUtils.deleteRecursive(backupDir)) { Log.w(TAG, "删除备份文件失败"); } emitter.onNext(SUCCESS); emitter.onComplete(); } catch (Exception e) { emitter.onError(e); } }); disposables.add( updateObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( result -> handleSuccess(), error -> handleError(error) ) ); } private void checkCancelled(ObservableEmitter emitter) { if (emitter.isDisposed() || isCancelled) { } } // 关键改进:统一的暂停/取消检查方法 private void checkInterrupted(ObservableEmitter<Integer> emitter) throws InterruptedException { while (isPaused && !emitter.isDisposed()) { Thread.sleep(500); } if (emitter.isDisposed() || isCancelled) { throw new InterruptedException("操作被取消"); } } // 取消更新(触发回滚) public void cancelrxUpdateManager() { isCancelled = true; disposables.clear(); // 取消订阅 rollback(); // 触发回滚 } // 暂停/恢复方法 public void pauseUpdate() { isPaused = true; } public void resumeUpdate() { isPaused = false; } // 成功处理(与AsyncTask的onPostExecute一致) private void handleSuccess() { if (backupAlreadyDir.exists() && !FileUtils.deleteRecursive(backupAlreadyDir)) { System.out.println("删除最终备份失败"); } notifyListener(SUCCESS, "更新成功,请重启车机"); currentPhase.set(PHASE_IDLE); rxUpdateManager = null; } // 错误处理(统一调用handleFailure) private void handleError(Throwable error) { if (error instanceof InterruptedException) { Log.w(TAG, "操作被取消", error); notifyListener(ERROR_USER_CANCELED, "用户取消"); } else { Log.e(TAG, "更新错误", error); notifyListener(ERROR_UNEXPECTED, "错误: " + error.getMessage()); } } private void rollback() { System.out.println("执行回滚操作"); // 回滚逻辑实现 handleFailure(ERROR_PHASE_ROLLBACK); } // private boolean dataVerification(String path, DecryptUtil.ProgressCallback callback) { // // 验签逻辑实现 // return true; // } // private String formatSize(long size) { // // 文件大小格式化 // return size + " bytes"; // } } // ================== 启动时恢复检查 ================== private void checkRecoveryOnStartup() { Log.i(TAG, "=== 启动时恢复检查 ===" ); if (backupDir != null && backupDir.exists()) { // 场景1:存在未完成的备份目录(backup) Log.w(TAG, "检测到残留备份目录: " + backupDir.getAbsolutePath() + ",该目录将被删除"); // 删除未完成的备份目录 if (!FileUtils.deleteRecursive(backupDir)) { Log.e(TAG, "无法删除未完成的备份目录: " + backupDir.getAbsolutePath()); } } handleFailure(ERROR_PHASE_ROLLBACK);//p4 // // // notifyProgress("数据恢复完成"); // this.updateListener = null; } private void checkStoragePerformance() { long writeSpeed = StorageUtils.measureWriteSpeed(storageDir); Log.d(TAG, "存储写入速度: " + formatSize(writeSpeed) + "/s"); if (writeSpeed < 50 * 1024 * 1024) { // 低于 50MB/s Log.w(TAG, "检测到低速存储设备,还原操作可能较慢"); } } //p3-7 p4-2 protected void handleFailure(Integer resultCode) { // 检测是否已经在处理失败回滚 if (ishandleFailure) { Log.w(TAG, "handleFailure is already running, skip duplicate call"); } else { if (backupDir != null && backupDir.exists()) { Log.w(TAG, "检测到残留备份目录: " + backupDir.getAbsolutePath() + ",该目录将被删除"); // 删除未完成的备份目录 if (FileUtils.deleteRecursive(backupDir)) { Log.i(TAG, "残留备份目录删除成功"); } else { Log.e(TAG, "无法删除未完成的备份目录: " + backupDir.getAbsolutePath()); } } rollbackTotalSize = FileUtils.getDirectorySize(backupAlreadyDir); Log.w(TAG, "handleFailure is running. " ); // 检测是否存在已完成的备份目录 if (backupAlreadyDir != null && backupAlreadyDir.exists()) { Log.w(TAG, "更新失败,正在回滚数据,回滚目录: " + backupAlreadyDir.getAbsolutePath()); currentPhase.set(PHASE_ROLLBACK); ishandleFailure = true; isRollingBack = true; // 启动回滚线程 new Thread(() -> { try { notifyProgress("更新失败,正在回滚数据..."); boolean rollbackSuccess = performRollback(backupAlreadyDir); if (rollbackSuccess) { Log.i(TAG, "备份回滚成功"); // 删除备份目录 if (backupAlreadyDir.exists() && !FileUtils.deleteRecursive(backupAlreadyDir)) { Log.w(TAG, "删除备份文件失败: " + backupAlreadyDir.getAbsolutePath()); } } else { Log.e(TAG, "备份回滚失败"); } } finally { // 回滚完成后,重置状态 if (!isCancelled) { notifyListener(resultCode, getErrorMessage(resultCode)); } } }).start(); } else { Log.w(TAG, "没有备份"); notifyListener(resultCode, lastErrorMessage); } } } // 重置状态 private void resetState() { ishandleFailure = false; isRollingBack = false; currentPhase.set(PHASE_IDLE); rxUpdateManager = null; updateListener = null; mProgress = 0; } //p3-6 private boolean performRollback(File backupAlreadyDir) { Log.w(TAG, "performRollback IS RUNNING "); try { if (rollbackTotalSize <= 0) { throw new IOException("备份目录为空或不可读: " + backupAlreadyDir.getAbsolutePath()); } // 设置回滚总大小 rollbackProcessedSize = 0; if (!storageDir.canWrite()) { throw new IOException("目标目录不可写: " + storageDir.getAbsolutePath()); } Log.w(TAG, "检查storage目录是否可写:"+storageDir.setWritable(true)); //打印storage的目录文件 // 删除当前升级后的数据 p3.4:回滚前置步骤删除失败->更改删除方法 long storageSizeTemp = FileUtils.getDirectorySize(storageDir); Log.w(TAG, "清空之前storageSize是:" + storageSizeTemp); if (storageDir.exists() && storageDir.isDirectory()) { Log.w(TAG, "通过判断:storageDir.exists() && storageDir.isDirectory()"); if (!FileUtils.deleteDirectoryContents(storageDir)) { throw new IOException("无法清空目标目录: " + storageDir.getAbsolutePath()); } } //打印storage的目录文件 storageSizeTemp = FileUtils.getDirectorySize(storageDir); Log.w(TAG, "清空结束后storageSize是:" + storageSizeTemp); Log.w(TAG, "目前的backup大小是:" + rollbackTotalSize); // 开始恢复备份 notifyProgress("开始恢复备份..."); // 执行目录复制并监听进度 Log.w(TAG, "准备执行:singlecopydirectory.copyDirectoryWithProgress"); //p7 boolean success = singlecopydirectory.copyDirectoryWithProgress(backupAlreadyDir, storageDir, (copied, total) -> { rollbackProcessedSize = copied; rollbackTotalSize = total; new Handler(Looper.getMainLooper()).post(() -> { notifyProgress("恢复中: " + formatSize(copied) + "/" + formatSize(total)); }); }); if (!success) { throw new IOException("恢复备份失败"); } ishandleFailure = false; resetState();//0903-重要- // 回滚完成 notifyProgress("数据恢复完成"); return true; } catch (Exception e) { Log.e(TAG, "回滚过程中发生错误", e); return false; } } // 释放资源 public void release() { try { context.unregisterReceiver(usbReceiver); context.unregisterReceiver(vehicleReceiver); } catch (Exception e) { Log.w(TAG, "释放资源时出错", e); } } // ================== 接口定义 ================== public interface UpdateListener { void onProgress(int phase, float progress, String message); void onResult(int resultCode, String message); } public interface ProgressCallback { void onProgress(long progress, long total) throws InterruptedException; } public File getUsbRoot() { return usbRoot; } public File getCacheDir() { return cacheDir; } public File getStorageDir() { return storageDir; } public String getErrorMessage(int code) { return switch (code) { case ERROR_NO_USB -> "未检测到USB设备,请检查连接状态或更换接口"; case ERROR_NO_UPDATE_PACKAGE -> "升级包文件缺失,请确认存储路径"; case ERROR_BATTERY_LOW -> "电池电量不足(需≥20%)"; case ERROR_STORAGE_INSUFFICIENT -> "存储空间不足(需预留20gb以上)"; case ERROR_UPDATE_IN_PROGRESS -> "系统正在执行其他升级任务"; case ERROR_COPY_FAILED -> "文件复制失败,请检查存储权限"; case ERROR_EXTRACT_FAILED -> "升级包解压失败(可能文件损坏)"; case ERROR_USER_CANCELED -> "用户已取消升级操作"; case ERROR_UNEXPECTED -> "发生未预期的系统异常"; case ERROR_USB_REMOVED -> "升级过程中USB设备被移除"; case ERROR_VEHICLE_SHIFTED -> "请将车辆档位切换至P档"; case ERROR_BATTERY_TOO_LOW -> "电池电量极低(需≥10%)"; case ERROR_FILE_VERIFY_FAILED -> "文件校验失败(MD5/SHA256不匹配)"; case ERROR_DECRYPT_OR_SIGN_FAILED -> "文件解密/验签失败"; case ERROR_PHASE_ROLLBACK -> "回滚成功"; default -> "未知错误导致更新失败"; }; } void removeLegacy() { if (storageDir == null || !storageDir.exists() || !storageDir.isDirectory()) { return; } Pattern pattern = Pattern.compile(FILE_NAME_PATTERN); File[] files = storageDir.listFiles(); if (files == null) return; for (File file : files) { if (file.isFile() && pattern.matcher(file.getName()).matches()) { // 删除匹配的文件 try { Files.deleteIfExists(file.toPath()); } catch (IOException | SecurityException e) { // 处理异常(记录日志等) } } } // 删除sign文件夹(如果存在) Path signDir = Paths.get(storageDir.getAbsolutePath(), "sign"); if (Files.exists(signDir)) { try { // 递归删除整个目录 FileUtils.deleteDirectoryRecursively(signDir); } catch (IOException | SecurityException e) { // 处理异常 } } } }
09-04
内容概要:本文详细介绍了一个基于Java和Vue的迁移学习与少样本图像分类系统的设计与实现,涵盖项目背景、目标、技术架构、核心算法、前后端代码实现、数据库设计、部署方案及应用领域。系统通过融合迁移学习与少样本学习技术,解决实际场景中样本稀缺、标注成本高、模型泛化能力差等问题,支持数据增强、预训练模型微调、原型网络(ProtoNet)等算法,并实现前后端分离、模块化设计、可视化监控与自动化工作流。项目提供完整的代码示例、API接口规范、数据库表结构及GUI界面,具备高扩展性、安全性和易用性,适用于医疗、工业、农业等多个领域。; 适合人群:具备一定Java、Vue和深度学习基础的研发人员、AI算法工程师、计算机相关专业学生及从事智能图像分析的科研人员。; 使用场景及目标:①在样本极少的场景下实现高精度图像分类,如医疗影像、工业缺陷检测;②构建可扩展、可视化的AI训练与推理平台;③学习如何将Python深度学习模型与Java后端集成,掌握前后端分离的AI系统开发流程;④了解迁移学习、少样本学习在实际工程中的落地方法。; 阅读建议:建议结合文档中的代码示例与流程图,搭建本地开发环境进行实践,重点关注前后端交互逻辑、Python模型服务调用机制及数据库设计,同时可基于项目结构扩展联邦学习、多模态融合等高级功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值