Handle初解,看完你就懂了handle

本文介绍了安卓开发中Handle的相关知识。Handle可通过消息队列发送和处理消息或Runnable对象,相当于搬运工。其主要作用是将子线程处理结果传递到UI线程,文中给出代码示例,并指出使用注意点,如Handle实现异步并非创建新线程等。


前言

提示:看完本篇,你可以了解到Handle的相关知识与常见的误区提醒


一、Handle是什么?

我们查看一下谷歌官方对其的定义:
A Handler allows you to send and process {@link Message} and Runnable
objects associated with a thread’s {@link MessageQueue}. Each Handler
instance is associated with a single thread and that thread’s message
queue. When you create a new Handler, it is bound to the thread /
message queue of the thread that is creating it – from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue.

Handler它可以通过消息队列发送消息或者runable对象,也可以处理消息或者runable对象。每一个handler实例,都持有一个线程以及此线程的消息队列。
那么handle相当于什么呢?其实就相当于我们现实世界的搬运工,可以发送消息,也可以处理消息。

二、Handle有啥用呢?

1.传递消息到ui线程

在我们安卓开发的时候,我们都知道ui线程是不可以进行耗时操作的,如果在这里进行的话,就会导致ANR,因此开辟一个子线程,在子线程中去处理相关的耗时任务,这是必然的结果。那么处理完的结果需要改变ui,即我们的控件显示的时候怎么办呢?在子线程中处理完直接改变ui是安卓中不允许的行为,因为ui线程不是线程安全的,那么我们有必要需要一个工具将子线程处理的结果带回去给ui线程去显示,而handle正是完成了这个任务。

2.代码示例

主线程定义一个handle(mHandle),构造方法里面new一个callback,复写handleMessage方法,这
就表明当有信息来的时候,由handleMessage方法去执行
      mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                //处理消息
                if(msg.what == 1) {
                    Log.d("hanldeTest","object is -- > " + msg.obj);
                }
                return false;
            }
        });
子线程中,假设处理完了一些耗时操作,比如读写数据库,随后即可执行一下代码
Message message = mHandler.obtainMessage();
        message.what = 1;
        message.obj = "我是新消息!";
        mHandler.sendMessage(message);

这样子一个发送,一个接受,就完成了就基本的handle使用,值得注意的是,上诉方法已经过期了
在这里插入图片描述
下面是最新的使用方法:

        boolean test=new Handler(getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
              //更新ui处理,在耗时任务完成后
            }
        });

这里细心观察的小伙伴就会发现,参数里面有一个getMainLooper(),字面意思就是获得主looper对吧,那么looper是个啥?拿了有啥用?我们先以过期的方法去解释更方便
handle源码:

    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

这里有个值得的两个对象,mLooper,mQueue。当系统创建一个ActivityThread的时候,也创建了一个Looper,在looper里面有一个消息队列(其实是单链表结构的东西),Looper负责不断循环保持我们的程序不会结束,queue则负责接受消息,然后有handle去处理。我们一个线程只有一个looper,负责当前线程的不中断,但是handle可以有多个,所有的handle都依附于某个消息队列(没有队列你要搬运工干嘛对吧),而队列由looper创建,这也相当于handle间接依赖于looper了嘛~(看源码mQueue = mLooper.mQueue;)
因为系统一开始就创建好ui线程的时候也自动帮我们创建好了looper,也所以我们在主线程创建的时候可以不用关心handle具体依附于哪个looper。
但是如果在子线程中呢?子线程则没有这样好的待遇,它没有自动生成looper,所以我们在普通子线程中创建handle不指定好特定的looper就会报错,因为handle要好好依赖于一个消息队列,而消息队列由我们的looper产生的嘛~因此就有了new Handler((放一个looper过来!))这个方法,如
new Handler(getMainLooper())。

3注意点

下面说一下几个注意点,我在面试的时候看到面试官也会问错的问题。handle是如何实现异步的?
可能有的同学就会说,异步?就创建一个子线程啊!其实handle,runnable之类的并不会创建一个新线程!

1、handle可以在主线程中创建,而正在使用发送信息是在子线程,而不是handle自己new了一个子线程
2、handle创建的时候依赖于传入的looper,如果传入的looper是ui线程的looper,那么当在子线程调用的时候,handle发送的消息也是发送到ui线程的消息队列里面,由消息队列取出来再处理,所以才会有这样一个异步的操作。线程的looper一直在调用.loop方法,去查询消息队列有没有东西,有的话就取出来。
源码如下

public static void loop() {
        final Looper me = myLooper();
		
		......
		
		//开始轮询,这个轮询可能阻塞
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
			
			......
			
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
			
			......
			
            msg.recycleUnchecked();
        }
    }

最后

看到这里你应该对handle处理消息是如何进行的吧,感谢各位看官~

<think> 我们正在处理一个大型AI系统的代码修复问题,根据提供的多个文件和日志,我们需要修复的主要问题包括: 1. 模型加载失败(路径问题) 2. 模块加载失败(抽象方法未实现、消息总线未始化) 3. 路径格式不一致(混合使用正斜杠和反斜杠) 我已经提供了关键修复代码片段,但用户要求提供完整文件以便直接替换。因此,我将提供以下文件的完整修复版本: - environment_interface.py - model_manager.py - cognitive_architecture.py - path_utils.py(新增) - 配置文件调整建议 注意:由于代码量较大,我会尽量保持代码完整,但省略部分非关键代码(用注释标注)。同时,我会在关键修改处添加注释说明。 由于用户要求不要省略,因此我将提供完整的文件内容,但为了阅读清晰,我会在每个文件开头标注关键修改点。 开始修复: </think> ### 完整修复代码(直接复制使用) #### 1. 环境接口模块 (environment_interface.py) ```python # E:\AI_System\agent\environment_interface.py import logging import time import queue import threading import json from typing import Any, Optional, Dict, List from agent.base_module import UnifiedCognitiveModule from core.message import Message, MessageType class EnvironmentInterface(UnifiedCognitiveModule): """环境交互接口 - 完整实现""" def __init__( self, name: str = "EnvironmentInterface", coordinator: Optional[Any] = None, config: Optional[Dict] = None ): super().__init__(name=name, coordinator=coordinator, config=config) # 配置参数 config = config or {} self.max_workers = config.get("max_workers", 4) self.response_timeout = config.get("response_timeout", 30.0) self.log_level = config.get("log_level", "INFO") # 日志配置 self.logger = logging.getLogger(name) log_level = getattr(logging, self.log_level.upper(), logging.INFO) self.logger.setLevel(log_level) # 输入输出队列 self.input_queue = queue.Queue() self.output_queue = queue.Queue() # 线程控制 self.running = True self.message_thread = threading.Thread(target=self._process_messages, daemon=True) self.message_thread.start() def shutdown(self) -> bool: """关闭环境接口""" try: self.running = False if self.message_thread.is_alive(): self.message_thread.join(timeout=2.0) super().shutdown() self.logger.info("🛑 环境接口已关闭") return True except Exception as e: self.logger.error(f"❌ 关闭失败: {str(e)}") return False def process(self, input_data: Any) -> dict: """处理输入数据""" if isinstance(input_data, dict): self.add_input(input_data) return {"status": "queued"} elif isinstance(input_data, str): self.add_input({"command": input_data}) return {"status": "queued"} else: return {"error": "不支持的输入类型"} def _process_messages(self): """处理消息的后台线程""" while self.running: try: # 获取用户输入 user_input = self.get_input(timeout=0.5) if user_input: # 发送到认知系统 message = Message( msg_type=MessageType.EVENT, sender=self.name, content=user_input, target="CognitiveSystem" ) self.send_message(message) self.logger.debug(f"📤 发送用户输入: {user_input['command'][:20]}...") # 处理输出队列 if not self.output_queue.empty(): output = self.output_queue.get_nowait() self._display_output(output) except Exception as e: self.logger.error(f"消息处理出错: {str(e)}") time.sleep(0.1) def handle_message(self, message: Message): """处理接收到的消息""" if message.target and message.target != self.name: return self.logger.debug(f"📩 收到消息 [{message.sender}]: {message.msg_type.name}") # 根据消息类型处理 if message.msg_type == MessageType.DATA: self.output(message.content) elif message.msg_type == MessageType.RESPONSE: self.output(message.content) elif message.msg_type == MessageType.STATUS: self.logger.info(f"系统状态更新: {message.content}") def get_input(self, timeout: float = 0.5) -> Optional[Dict]: """获取输入""" try: return self.input_queue.get(timeout=timeout) except queue.Empty: return None def output(self, response: Any): """添加响应""" self.output_queue.put(response) def _display_output(self, response: Any): """格式化并显示输出""" try: # 处理不同类型的响应 if isinstance(response, dict): response.setdefault("timestamp", time.time()) response.setdefault("source", "system") response_str = json.dumps(response, ensure_ascii=False, indent=2) print(f"<< {response_str}") if "message" in response: self.logger.info(f"💬 系统响应: {response['message']}") else: self.logger.info("💬 系统响应: 无内容") elif isinstance(response, str): print(f"<< {response}") self.logger.info(f"💬 系统响应: {response}") elif isinstance(response, Message): print(f"<< [Message from {response.sender}]: {response.content[:50]}...") self.logger.info(f"💬 收到消息响应: {response.content[:50]}...") else: response_str = str(response) print(f"<< {response_str}") self.logger.info(f"💬 系统响应: {response_str}") except Exception as e: self.logger.error(f"输出响应失败: {str(e)}") def add_input(self, input_data: dict): """添加新输入""" if not isinstance(input_data, dict): self.logger.error("输入数据格式错误,必须是字典") return input_data.setdefault("timestamp", time.time()) input_data.setdefault("source", "user") self.input_queue.put(input_data) self.logger.debug(f"手动添加输入: {input_data['command'][:20]}...") def get_health_status(self) -> dict: """返回模块健康状态""" return { "status": "running" if self.running else "stopped", "module": self.name, "queue_size": self.input_queue.qsize(), "last_activity": time.time(), "output_queue_size": self.output_queue.qsize() } ``` #### 2. 模型管理器 (model_manager.py) ```python # E:\AI_System\agent\model_manager.py import os import sys import logging import json import hashlib import gc import time from pathlib import Path from typing import Dict, Any, Optional, Tuple, List from utils.path_utils import normalize_path, is_valid_hf_id class ModelManager: """AI模型管理器 - 完整修复版""" MODEL_REGISTRY_FILE = "model_registry.json" DEFAULT_MODEL_PATHS = { "TEXT_BASE": "local_models/text_base", "TEXT_CHAT": "local_models/text_chat", "IMAGE_MODEL": "local_models/image_model" } def __init__(self, config: Dict[str, Any] = None, cache_dir: str = "model_cache", use_gpu: bool = True, max_models_in_memory: int = 3): # 配置日志 self.logger = logging.getLogger("ModelManager") self.logger.setLevel(logging.INFO) if not self.logger.handlers: handler = logging.StreamHandler() formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) handler.setFormatter(formatter) self.logger.addHandler(handler) self.logger.info("🚀 始化模型管理器...") # 始化参数 self.config = config or {} self.cache_dir = normalize_path(cache_dir) # 使用路径规范化 self.use_gpu = use_gpu self.max_models_in_memory = max_models_in_memory # 确保缓存目录存在 os.makedirs(self.cache_dir, exist_ok=True) # 加载或创建注册表 self._persistent_registry = self._load_or_create_registry() # 已加载的模型 self.loaded_models: Dict[str, Any] = {} # 自动注册默认模型 self._register_default_models() self.logger.info(f"✅ 模型管理器始化完成 (GPU: {'启用' if use_gpu else '禁用'})") self.logger.info(f"已注册模型: {list(self._persistent_registry.keys())}") def _load_or_create_registry(self) -> Dict[str, dict]: """加载或创建模型注册表""" try: registry_path = Path(normalize_path(self.MODEL_REGISTRY_FILE)) # 路径规范化 if registry_path.exists(): with open(registry_path, 'r', encoding='utf-8') as f: registry = json.load(f) self.logger.info(f"📋 成功加载模型注册表: {registry_path}") return registry self.logger.warning(f"⚠️ 模型注册表不存在,创建新文件: {registry_path}") with open(registry_path, 'w', encoding='utf-8') as f: json.dump({}, f, indent=2) return {} except Exception as e: self.logger.error(f"❌ 处理模型注册表失败: {str(e)}") return {} def _register_default_models(self): """注册配置文件中的默认模型""" model_settings = self.config.get("model_settings", {}) # 合并默认路径和配置路径 default_paths = {**self.DEFAULT_MODEL_PATHS, **{ name: info.get("path", self.DEFAULT_MODEL_PATHS.get(name, "")) for name, info in model_settings.items() }} # 注册模型 for model_name, model_path in default_paths.items(): if model_name not in self._persistent_registry: abs_path = normalize_path(model_path) # 路径规范化 model_type = model_settings.get(model_name, {}).get("type", "text") self.register_model(model_name, abs_path, model_type) def _save_registry(self): """保存模型注册表""" try: registry_path = normalize_path(self.MODEL_REGISTRY_FILE) # 路径规范化 with open(registry_path, 'w', encoding='utf-8') as f: json.dump(self._persistent_registry, f, indent=2, ensure_ascii=False) self.logger.info(f"💾 模型注册表已保存: {registry_path}") return True except Exception as e: self.logger.error(f"❌ 保存模型注册表失败: {str(e)}") return False def register_model(self, model_name: str, model_path: str, model_type: str = "text", adapter_config: Optional[dict] = None) -> bool: """ 注册新模型 """ # 检查模型是否存在 exists, is_local = self._check_model_exists(model_path) if not exists: self.logger.error(f"❌ 模型路径不可访问: {model_path}") return False # 计算校验和 checksum = "unknown" if is_local: try: checksum = self._calculate_checksum(model_path) except Exception as e: self.logger.warning(f"⚠️ 无法计算校验和: {str(e)}") checksum = "error" # 添加到注册表 self._persistent_registry[model_name] = { "path": model_path, "type": model_type, "status": "unloaded", "checksum": checksum, "last_accessed": time.time(), "adapter": adapter_config, "is_local": is_local } self.logger.info(f"✅ 模型注册成功: {model_name} ({model_type})") self._save_registry() return True def _check_model_exists(self, model_path: str) -> Tuple[bool, bool]: """检查模型路径是否有效""" # 如果是HuggingFace模型ID if is_valid_hf_id(model_path): # 使用路径工具验证 self.logger.info(f"🔍 检测到HuggingFace模型ID: {model_path}") return True, False # 检查本地路径 abs_path = normalize_path(model_path) # 路径规范化 if os.path.exists(abs_path): return True, True # 尝试相对路径 if os.path.exists(model_path): return True, True return False, False def _calculate_checksum(self, model_path: str) -> str: """计算模型校验和""" abs_path = normalize_path(model_path) # 路径规范化 if os.path.isdir(abs_path): sha256 = hashlib.sha256() key_files = ["pytorch_model.bin", "model.safetensors", "config.json"] for root, _, files in os.walk(abs_path): for file in files: if file in key_files: file_path = os.path.join(root, file) with open(file_path, 'rb') as f: while chunk := f.read(8192): sha256.update(chunk) return sha256.hexdigest() # 单个模型文件 with open(abs_path, 'rb') as f: return hashlib.sha256(f.read()).hexdigest() def load_model(self, model_name: str, force_reload: bool = False) -> Tuple[bool, Any]: """ 加载模型到内存 """ if model_name not in self._persistent_registry: self.logger.error(f"❌ 模型未注册: {model_name}") return False, None model_info = self._persistent_registry[model_name] model_path = model_info["path"] abs_path = normalize_path(model_path) if model_info.get("is_local", True) else model_path # 路径规范化 # 如果模型已加载且不需要强制重载 if model_name in self.loaded_models and not force_reload: self.logger.info(f"📦 模型已在内存中: {model_name}") model_info["last_accessed"] = time.time() return True, self.loaded_models[model_name] # 检查内存占用 if len(self.loaded_models) >= self.max_models_in_memory: self._unload_least_recently_used() # 实际加载模型 try: self.logger.info(f"🔄 加载模型: {model_name} ({model_info['type']})") model_type = model_info["type"] if model_type == "text": model = self._load_text_model(model_info, abs_path) elif model_type == "image": model = self._load_image_model(model_info, abs_path) elif model_type == "audio": model = self._load_audio_model(model_info, abs_path) else: self.logger.error(f"❌ 不支持的模型类型: {model_type}") return False, None # 更新状态 self.loaded_models[model_name] = model model_info["status"] = "loaded" model_info["last_accessed"] = time.time() self._save_registry() self.logger.info(f"✅ 模型加载成功: {model_name}") return True, model except ImportError as e: self.logger.error(f"❌ 缺失依赖库: {str(e)}") return False, None except Exception as e: self.logger.error(f"❌ 模型加载失败: {model_name}, 路径: {abs_path}, 错误: {str(e)}") model_info["status"] = "error" return False, None def _load_text_model(self, model_info: dict, model_path: str) -> Any: """加载文本模型""" try: from transformers import AutoModelForCausalLM, AutoTokenizer except ImportError: self.logger.error("❌ transformers库未安装") raise RuntimeError("transformers not installed") self.logger.debug(f"🔧 加载文本模型: {model_path}") device = "cuda" if self.use_gpu else "cpu" try: tokenizer = AutoTokenizer.from_pretrained(model_path, cache_dir=self.cache_dir) model = AutoModelForCausalLM.from_pretrained( model_path, cache_dir=self.cache_dir, device_map=device if self.use_gpu else None ) return { "model": model, "tokenizer": tokenizer, "info": model_info } except OSError as e: self.logger.error(f"❌ 加载失败: 请检查路径 '{model_path}' 是否正确") fallback_path = self._try_find_model_path(model_path) if fallback_path: self.logger.warning(f"⚠️ 尝试备用路径: {fallback_path}") return self._load_text_model(model_info, fallback_path) raise except Exception as e: self.logger.error(f"❌ 加载过程中发生意外错误: {str(e)}") raise def _try_find_model_path(self, original_path: str) -> Optional[str]: """尝试找到备用模型路径""" # 1. 检查项目内的模型目录 project_models = os.path.join(os.getcwd(), "local_models", os.path.basename(original_path)) if os.path.exists(project_models): return project_models # 2. 检查缓存目录 cache_path = os.path.join(self.cache_dir, "models", os.path.basename(original_path)) if os.path.exists(cache_path): return cache_path # 3. 尝试父目录 parent_path = os.path.join(os.path.dirname(os.getcwd()), os.path.basename(original_path)) if os.path.exists(parent_path): return parent_path return None def unload_model(self, model_name: str = None) -> bool: """卸载模型""" if model_name is None: self.logger.info("卸载所有模型") for name in list(self.loaded_models.keys()): if not self._unload_single_model(name
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值